summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--Documentation/dev-eclipse.txt27
-rw-r--r--Documentation/dev-readme.txt5
-rw-r--r--Documentation/install.txt2
-rw-r--r--gerrit-common/.gitignore5
-rw-r--r--gerrit-common/.settings/org.eclipse.core.resources.prefs (renamed from .settings/org.eclipse.core.resources.prefs)0
-rw-r--r--gerrit-common/.settings/org.eclipse.core.runtime.prefs (renamed from .settings/org.eclipse.core.runtime.prefs)0
-rw-r--r--gerrit-common/.settings/org.eclipse.jdt.core.prefs (renamed from .settings/org.eclipse.jdt.core.prefs)0
-rw-r--r--gerrit-common/.settings/org.eclipse.jdt.ui.prefs (renamed from .settings/org.eclipse.jdt.ui.prefs)0
-rw-r--r--gerrit-common/pom.xml85
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/Common.gwt.xml20
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java68
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInMode.java19
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInRequired.java33
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/DiscoveryResult.java37
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdService.java27
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdUrls.java24
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/LoginResult.java20
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java25
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java71
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java68
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfoCache.java79
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountProjectWatchInfo.java39
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java68
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java53
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AddReviewerResult.java79
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java43
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalDetail.java85
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummary.java44
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummarySet.java49
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java110
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java67
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java177
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java31
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java92
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java96
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java29
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java195
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/EditList.java169
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java142
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java84
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java68
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java46
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java23
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java66
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java96
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScriptSettings.java68
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java54
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java97
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java61
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java43
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectInfo.java36
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/SingleListChangeInfo.java52
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/SparseFileContent.java153
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/SshHostKey.java46
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java33
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java31
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/data/ToggleStarRequest.java63
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java30
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/CorruptEntityException.java28
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidNameException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidRevisionException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshKeyException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshUserNameException.java30
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchAccountException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchEntityException.java26
-rw-r--r--gerrit-common/src/main/java/com/google/gerrit/common/errors/NotSignedInException.java26
-rw-r--r--gerrit-gwtdebug/.gitignore4
-rw-r--r--gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-gwtdebug/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-gwtdebug/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-gwtdebug/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-gwtdebug/pom.xml66
-rw-r--r--gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java419
-rw-r--r--gerrit-gwtui/.gitignore4
-rw-r--r--gerrit-gwtui/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-gwtui/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-gwtui/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-gwtui/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-gwtui/pom.xml259
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml32
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml (renamed from src/main/java/com/google/gerrit/UserAgent.gwt.xml)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java (renamed from src/main/java/com/google/gerrit/client/ErrorDialog.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java119
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java404
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java (renamed from src/main/java/com/google/gerrit/client/GerritConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties (renamed from src/main/java/com/google/gerrit/client/GerritConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritIcons.java (renamed from src/main/java/com/google/gerrit/client/GerritIcons.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java (renamed from src/main/java/com/google/gerrit/client/GerritMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties (renamed from src/main/java/com/google/gerrit/client/GerritMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/HistoryHandler.java292
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java25
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java70
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java (renamed from src/main/java/com/google/gerrit/client/NotFoundScreen.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java (renamed from src/main/java/com/google/gerrit/client/NotSignedInDialog.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java (renamed from src/main/java/com/google/gerrit/client/RpcStatus.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java145
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/SignInDialog.java45
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java (renamed from src/main/java/com/google/gerrit/client/account/AccountConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties (renamed from src/main/java/com/google/gerrit/client/account/AccountConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java (renamed from src/main/java/com/google/gerrit/client/account/AccountMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties (renamed from src/main/java/com/google/gerrit/client/account/AccountMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountSettings.java168
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AgreementPanel.java139
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java130
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java353
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java243
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java50
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java276
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/PreferencePanel.java202
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java313
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java105
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java49
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java588
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java35
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java534
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java (renamed from src/main/java/com/google/gerrit/client/admin/AdminConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties (renamed from src/main/java/com/google/gerrit/client/admin/AdminConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java (renamed from src/main/java/com/google/gerrit/client/admin/AdminMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties (renamed from src/main/java/com/google/gerrit/client/admin/AdminMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java97
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java104
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java107
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java288
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java205
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java134
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java438
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/arrowRight.gif (renamed from src/main/java/com/google/gerrit/client/arrowRight.gif)bin78 -> 78 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java (renamed from src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties (renamed from src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java (renamed from src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java (renamed from src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties (renamed from src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java333
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java32
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif (renamed from src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif)bin600 -> 600 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif (renamed from src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif)bin89 -> 89 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png (renamed from src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png)bin6478 -> 6478 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java (renamed from src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties (renamed from src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java (renamed from src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties (renamed from src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java229
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/Util.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java106
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java88
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java44
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java44
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java (renamed from src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java162
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java284
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java44
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java (renamed from src/main/java/com/google/gerrit/client/changes/ChangeConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties (renamed from src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java94
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java (renamed from src/main/java/com/google/gerrit/client/changes/ChangeMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties (renamed from src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties (renamed from src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java70
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java402
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java490
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MessagePanel.java33
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java38
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java64
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java38
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java311
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java560
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java326
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java60
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png (renamed from src/main/java/com/google/gerrit/client/greenCheck.png)bin285 -> 285 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java610
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java338
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java211
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java119
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java (renamed from src/main/java/com/google/gerrit/client/patches/PatchConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties (renamed from src/main/java/com/google/gerrit/client/patches/PatchConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java (renamed from src/main/java/com/google/gerrit/client/patches/PatchLine.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java (renamed from src/main/java/com/google/gerrit/client/patches/PatchMessages.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties (renamed from src/main/java/com/google/gerrit/client/patches/PatchMessages.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties (renamed from src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java556
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java30
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java290
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java357
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png (renamed from src/main/java/com/google/gerrit/client/redNot.png)bin308 -> 308 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java83
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java (renamed from src/main/java/com/google/gerrit/client/rpc/RpcConstants.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties (renamed from src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java51
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/starFilled.gif (renamed from src/main/java/com/google/gerrit/client/starFilled.gif)bin171 -> 171 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/starOpen.gif (renamed from src/main/java/com/google/gerrit/client/starOpen.gif)bin179 -> 179 bytes
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java54
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java (renamed from src/main/java/com/google/gerrit/client/ui/AccountScreen.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java (renamed from src/main/java/com/google/gerrit/client/ui/AddMemberBox.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java47
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java (renamed from src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java193
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java (renamed from src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java (renamed from src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java (renamed from src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java (renamed from src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java (renamed from src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java (renamed from src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java (renamed from src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java (renamed from src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java (renamed from src/main/java/com/google/gerrit/client/ui/NavigationTable.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java (renamed from src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PatchLink.java65
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java56
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java63
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java101
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java (renamed from src/main/java/com/google/gerrit/client/ui/SmallHeading.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java31
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java (renamed from src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/common/Version.java26
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/public/gerrit.css (renamed from src/main/java/com/google/gerrit/public/gerrit.css)0
-rw-r--r--gerrit-gwtui/src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif (renamed from src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif)bin237 -> 237 bytes
-rw-r--r--gerrit-gwtui/src/main/webapp/WEB-INF/web.xml3
-rw-r--r--gerrit-httpd/.gitignore4
-rw-r--r--gerrit-httpd/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-httpd/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-httpd/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-httpd/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-httpd/pom.xml67
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java98
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java126
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java245
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCanonicalWebUrlProvider.java81
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCurrentUserProvider.java35
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpIdentifiedUserProvider.java42
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java57
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRemotePeerProvider.java48
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestCleanupFilter.java59
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java132
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java122
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSession.java177
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java212
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html (renamed from src/main/webapp/WEB-INF/BecomeAnyAccount.html)0
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java175
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java116
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthModule.java26
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java171
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/LoginRedirect.html (renamed from src/main/webapp/WEB-INF/LoginRedirect.html)0
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LdapAuthModule.java33
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LoginRedirectServlet.java76
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/UserPassAuthServiceImpl.java67
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java57
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java53
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java467
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java326
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPage.html (renamed from src/main/webapp/WEB-INF/HostPage.html)0
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java249
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerrit.html (renamed from src/main/webapp/WEB-INF/LegacyGerrit.html)0
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java82
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/PrettifyServlet.java90
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java100
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java157
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java123
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java598
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java124
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java45
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/Handler.java94
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/RpcServletModule.java50
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java125
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java78
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java39
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java41
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java385
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java172
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java80
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/ExternalIdDetailFactory.java53
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java363
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupDetailFactory.java111
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java59
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java143
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java240
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java54
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java43
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java41
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java127
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java155
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java158
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/AddReviewer.java158
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/CommentDetailFactory.java134
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java419
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchModule.java39
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java390
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java236
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java103
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java185
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddProjectRight.java129
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java81
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java121
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteProjectRights.java79
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListBranches.java108
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/OwnedProjects.java84
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java104
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java125
-rw-r--r--gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java43
-rw-r--r--gerrit-main/.gitignore4
-rw-r--r--gerrit-main/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-main/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-main/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-main/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-main/pom.xml34
-rw-r--r--gerrit-main/src/main/java/Main.java29
-rw-r--r--gerrit-main/src/main/java/com/google/gerrit/main/GerritLauncher.java441
-rw-r--r--gerrit-patch-commonsnet/.gitignore4
-rw-r--r--gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-patch-commonsnet/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-patch-commonsnet/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-patch-commonsnet/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-patch-commonsnet/pom.xml56
-rw-r--r--gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java207
-rw-r--r--gerrit-patch-gwtexpui/.gitignore4
-rw-r--r--gerrit-patch-gwtexpui/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-patch-gwtexpui/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-patch-gwtexpui/pom.xml47
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/PrettyFormatter.gwt.xml19
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java (renamed from src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java (renamed from src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-css.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-css.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-hs.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-hs.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lisp.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-lisp.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lua.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-lua.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-ml.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-ml.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-proto.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-proto.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-sql.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-sql.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-vb.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-vb.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-wiki.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/lang-wiki.js)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.css (renamed from src/main/java/com/google/gerrit/public/prettify20090521/prettify.css)0
-rw-r--r--gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.js (renamed from src/main/java/com/google/gerrit/public/prettify20090521/prettify.js)0
-rw-r--r--gerrit-patch-jgit/.gitignore4
-rw-r--r--gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-patch-jgit/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-patch-jgit/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-patch-jgit/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-patch-jgit/pom.xml52
-rw-r--r--gerrit-patch-jgit/src/main/java/org/eclipse/jgit/JGit.gwt.xml (renamed from src/main/java/org/eclipse/jgit/JGit.gwt.xml)0
-rw-r--r--gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/EditDeserializer.java (renamed from src/main/java/org/eclipse/jgit/diff/EditDeserializer.java)0
-rw-r--r--gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java (renamed from src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java)0
-rw-r--r--gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java58
-rw-r--r--gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java (renamed from src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java)0
-rw-r--r--gerrit-pgm/.gitignore4
-rw-r--r--gerrit-pgm/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-pgm/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-pgm/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-pgm/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-pgm/pom.xml51
-rw-r--r--gerrit-pgm/src/main/java/com/google/gerrit/pgm/AbstractProgram.java99
-rw-r--r--gerrit-pgm/src/main/java/com/google/gerrit/pgm/CreateSchema.java64
-rw-r--r--gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java56
-rw-r--r--gerrit-pgm/src/main/java/com/google/gerrit/pgm/Version.java29
-rw-r--r--gerrit-reviewdb/.gitignore4
-rw-r--r--gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-reviewdb/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-reviewdb/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-reviewdb/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-reviewdb/pom.xml41
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/ReviewDB.gwt.xml19
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AbstractAgreement.java59
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Account.java207
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAccess.java51
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreement.java118
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java30
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalId.java158
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java41
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java96
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java229
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java45
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreement.java114
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java31
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMember.java77
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAccess.java33
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAudit.java102
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAuditAccess.java32
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReview.java72
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReviewAccess.java31
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatch.java109
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatchAccess.java42
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKey.java153
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKeyAccess.java33
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java169
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryAccess.java34
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValue.java108
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValueAccess.java31
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AuthType.java52
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Branch.java101
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Change.java451
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeAccess.java90
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessage.java114
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessageAccess.java30
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CodedEnum.java20
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContactInformation.java85
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreement.java139
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreementAccess.java31
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java272
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineComment.java177
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineCommentAccess.java65
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSet.java159
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAccess.java40
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestor.java88
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestorAccess.java34
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApproval.java145
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApprovalAccess.java50
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetInfo.java82
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java188
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java37
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRight.java109
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRightAccess.java34
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RevId.java57
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java135
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java61
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java26
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChange.java69
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChangeAccess.java33
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java79
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfigAccess.java31
-rw-r--r--gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/UserIdentity.java74
-rw-r--r--gerrit-server/.gitignore4
-rw-r--r--gerrit-server/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-server/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-server/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-server/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-server/pom.xml131
-rw-r--r--gerrit-server/src/main/antlr/com/google/gerrit/server/query/Query.g (renamed from src/main/antlr/com/google/gerrit/server/query/Query.g)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/common/Version.java52
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java (renamed from src/main/java/com/google/gerrit/server/AccessPath.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java48
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java84
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java66
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/FileTypeRegistry.java (renamed from src/main/java/com/google/gerrit/server/FileTypeRegistry.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdent.java (renamed from src/main/java/com/google/gerrit/server/GerritPersonIdent.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java (renamed from src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java238
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java (renamed from src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ParamertizedString.java (renamed from src/main/java/com/google/gerrit/server/ParamertizedString.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/RemotePeer.java (renamed from src/main/java/com/google/gerrit/server/RemotePeer.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java56
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/RequestCleanup.java (renamed from src/main/java/com/google/gerrit/server/RequestCleanup.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/UrlEncoded.java (renamed from src/main/java/com/google/gerrit/server/UrlEncoded.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java26
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java111
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java24
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java135
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountException.java (renamed from src/main/java/com/google/gerrit/server/account/AccountException.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java75
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java301
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java86
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java71
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java107
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/AuthResult.java43
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java73
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/EmailExpander.java (renamed from src/main/java/com/google/gerrit/server/account/EmailExpander.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java30
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java163
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java92
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java36
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java46
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java44
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java130
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java587
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java152
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/SearchScope.java46
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java (renamed from src/main/java/com/google/gerrit/server/cache/Cache.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java (renamed from src/main/java/com/google/gerrit/server/cache/CacheModule.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java (renamed from src/main/java/com/google/gerrit/server/cache/CachePool.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java (renamed from src/main/java/com/google/gerrit/server/cache/CacheProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java (renamed from src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java (renamed from src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java (renamed from src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java (renamed from src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java (renamed from src/main/java/com/google/gerrit/server/cache/SimpleCache.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java (renamed from src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java69
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java193
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java (renamed from src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java (renamed from src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java (renamed from src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java (renamed from src/main/java/com/google/gerrit/server/config/ConfigUtil.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java46
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java (renamed from src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java (renamed from src/main/java/com/google/gerrit/server/config/FactoryModule.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java38
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java163
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java52
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfig.java (renamed from src/main/java/com/google/gerrit/server/config/GerritServerConfig.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java (renamed from src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/Nullable.java (renamed from src/main/java/com/google/gerrit/server/config/Nullable.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java63
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java (renamed from src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java44
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/SitePath.java (renamed from src/main/java/com/google/gerrit/server/config/SitePath.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathProvider.java37
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java338
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectName.java33
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java45
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java26
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java84
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java311
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java33
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java197
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/CodeReviewCommit.java73
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/CommitMergeStatus.java47
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultQueueOp.java29
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java175
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java28
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java1189
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/MergeQueue.java27
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSorter.java92
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/PatchSetImporter.java127
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java91
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/PushOp.java291
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java433
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/ReloadSubmitQueueOp.java72
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/ReplicationQueue.java59
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java245
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java175
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java48
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java38
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/Address.java (renamed from src/main/java/com/google/gerrit/server/mail/Address.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java132
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java82
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailException.java (renamed from src/main/java/com/google/gerrit/server/mail/EmailException.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java (renamed from src/main/java/com/google/gerrit/server/mail/EmailHeader.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailSender.java (renamed from src/main/java/com/google/gerrit/server/mail/EmailSender.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java22
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java136
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java52
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java178
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java137
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java632
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/RecipientType.java (renamed from src/main/java/com/google/gerrit/server/mail/RecipientType.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java (renamed from src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java149
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java35
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java213
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java115
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java164
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java28
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java200
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java263
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java111
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java117
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java (renamed from src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java46
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java134
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java28
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java28
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java31
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java119
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java227
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java107
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java (renamed from src/main/java/com/google/gerrit/server/query/AndPredicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java79
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java (renamed from src/main/java/com/google/gerrit/server/query/NotPredicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java (renamed from src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java (renamed from src/main/java/com/google/gerrit/server/query/OperatorPredicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java (renamed from src/main/java/com/google/gerrit/server/query/OrPredicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java (renamed from src/main/java/com/google/gerrit/server/query/Predicate.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java (renamed from src/main/java/com/google/gerrit/server/query/QueryBuilder.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/query/QueryParseException.java (renamed from src/main/java/com/google/gerrit/server/query/QueryParseException.java)0
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshInfo.java23
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java26
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java100
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java261
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java63
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java33
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java66
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/ParamertizedStringTest.java (renamed from src/test/java/com/google/gerrit/server/ParamertizedStringTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java362
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java (renamed from src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java (renamed from src/test/java/com/google/gerrit/server/mail/AddressTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java285
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java32
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java (renamed from src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java (renamed from src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java (renamed from src/test/java/com/google/gerrit/server/query/NotPredicateTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java (renamed from src/test/java/com/google/gerrit/server/query/QueryParserTest.java)0
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java105
-rw-r--r--gerrit-sshd/.gitignore4
-rw-r--r--gerrit-sshd/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-sshd/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-sshd/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-sshd/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-sshd/pom.xml61
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java32
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java454
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java100
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java74
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandName.java35
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java139
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java80
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java130
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommandProvider.java97
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java77
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java67
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshCurrentUserProvider.java44
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java503
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheEntry.java65
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java156
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java128
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScopes.java117
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java135
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java57
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java64
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java56
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java81
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AbstractGitCommand.java68
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java150
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminFlushCaches.java124
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminReplicate.java87
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowCaches.java207
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowConnections.java167
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowQueue.java133
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveCommand.java337
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java138
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java50
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java54
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ErrorSlaveMode.java40
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java76
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java33
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java1440
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java350
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SlaveCommandModule.java33
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java28
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/TOC (renamed from src/main/java/com/google/gerrit/server/ssh/scproot/TOC)0
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/bin/gerrit-cherry-pick (renamed from src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick)0
-rw-r--r--gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/hooks/commit-msg (renamed from src/main/java/com/google/gerrit/server/ssh/scproot/hooks/commit-msg)0
-rw-r--r--gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/CommitMsgHookTest.java367
-rw-r--r--gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/HookTestCase.java102
-rw-r--r--gerrit-sshd/src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java (renamed from src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java)0
-rw-r--r--gerrit-sshd/src/test/java/com/google/gerrit/testutil/MockSystemReader.java (renamed from src/test/java/com/google/gerrit/testutil/MockSystemReader.java)0
-rw-r--r--gerrit-util-cli/.gitignore4
-rw-r--r--gerrit-util-cli/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-util-cli/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-util-cli/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-util-cli/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-util-cli/pom.xml51
-rw-r--r--gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java170
-rw-r--r--gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerFactory.java26
-rw-r--r--gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerUtil.java36
-rw-r--r--gerrit-util-ssl/.gitignore4
-rw-r--r--gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-util-ssl/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-util-ssl/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-util-ssl/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-util-ssl/pom.xml34
-rw-r--r--gerrit-util-ssl/src/main/java/com/google/gerrit/util/ssl/BlindSSLSocketFactory.java112
-rw-r--r--gerrit-war/.gitignore4
-rw-r--r--gerrit-war/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--gerrit-war/.settings/org.eclipse.core.runtime.prefs3
-rw-r--r--gerrit-war/.settings/org.eclipse.jdt.core.prefs268
-rw-r--r--gerrit-war/.settings/org.eclipse.jdt.ui.prefs61
-rw-r--r--gerrit-war/pom.xml150
-rw-r--r--gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java228
-rw-r--r--gerrit-war/src/main/java/log4j.properties (renamed from src/main/java/log4j.properties)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/GerritServer.properties_example (renamed from src/main/java/GerritServer.properties_example)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh (renamed from src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml (renamed from src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml (renamed from src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh (renamed from src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml (renamed from src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml (renamed from src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/index_generic.sql (renamed from src/main/webapp/WEB-INF/sql/index_generic.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/index_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/index_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/mysql_nextval.sql (renamed from src/main/webapp/WEB-INF/sql/mysql_nextval.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade003_004.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade003_004.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade005_006.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade005_006.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade006_007.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade006_007.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade007_008.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade007_008.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade008_009.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade008_009.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade009_010.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade009_010.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade010_011.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade010_011.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql (renamed from src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql)0
-rw-r--r--gerrit-war/src/main/webapp/WEB-INF/web.xml21
-rw-r--r--gerrit-war/src/main/webapp/favicon.ico (renamed from src/main/webapp/favicon.ico)bin318 -> 318 bytes
-rw-r--r--gerrit-war/src/main/webapp/robots.txt (renamed from src/main/webapp/robots.txt)0
-rw-r--r--gerrit_debug.launch27
-rw-r--r--gerrit_macos.launch27
-rw-r--r--pom.xml883
-rw-r--r--src/main/java/com/google/gerrit/Gerrit.gwt.xml33
-rw-r--r--src/main/java/com/google/gerrit/client/FormatUtil.java119
-rw-r--r--src/main/java/com/google/gerrit/client/Gerrit.java400
-rw-r--r--src/main/java/com/google/gerrit/client/GerritVersion.java26
-rw-r--r--src/main/java/com/google/gerrit/client/HostPageData.java24
-rw-r--r--src/main/java/com/google/gerrit/client/HostPageDataService.java24
-rw-r--r--src/main/java/com/google/gerrit/client/JumpKeys.java69
-rw-r--r--src/main/java/com/google/gerrit/client/Link.java324
-rw-r--r--src/main/java/com/google/gerrit/client/SearchPanel.java144
-rw-r--r--src/main/java/com/google/gerrit/client/SignInDialog.java48
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountProjectWatchInfo.java39
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountSecurity.java68
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountService.java53
-rw-r--r--src/main/java/com/google/gerrit/client/account/AccountSettings.java168
-rw-r--r--src/main/java/com/google/gerrit/client/account/AgreementInfo.java43
-rw-r--r--src/main/java/com/google/gerrit/client/account/AgreementPanel.java138
-rw-r--r--src/main/java/com/google/gerrit/client/account/ContactPanelFull.java130
-rw-r--r--src/main/java/com/google/gerrit/client/account/ContactPanelShort.java353
-rw-r--r--src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java242
-rw-r--r--src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java50
-rw-r--r--src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java275
-rw-r--r--src/main/java/com/google/gerrit/client/account/PreferencePanel.java202
-rw-r--r--src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java312
-rw-r--r--src/main/java/com/google/gerrit/client/account/RegisterScreen.java105
-rw-r--r--src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java49
-rw-r--r--src/main/java/com/google/gerrit/client/account/SshPanel.java588
-rw-r--r--src/main/java/com/google/gerrit/client/account/Util.java33
-rw-r--r--src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java51
-rw-r--r--src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java533
-rw-r--r--src/main/java/com/google/gerrit/client/admin/GroupAdminService.java68
-rw-r--r--src/main/java/com/google/gerrit/client/admin/GroupDetail.java47
-rw-r--r--src/main/java/com/google/gerrit/client/admin/GroupListScreen.java96
-rw-r--r--src/main/java/com/google/gerrit/client/admin/GroupTable.java104
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java106
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java61
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java288
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectDetail.java43
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java204
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java133
-rw-r--r--src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java437
-rw-r--r--src/main/java/com/google/gerrit/client/admin/Util.java52
-rw-r--r--src/main/java/com/google/gerrit/client/auth/openid/DiscoveryResult.java37
-rw-r--r--src/main/java/com/google/gerrit/client/auth/openid/OpenIdService.java27
-rw-r--r--src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java330
-rw-r--r--src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java44
-rw-r--r--src/main/java/com/google/gerrit/client/auth/userpass/LoginResult.java20
-rw-r--r--src/main/java/com/google/gerrit/client/auth/userpass/UserPassAuthService.java25
-rw-r--r--src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java227
-rw-r--r--src/main/java/com/google/gerrit/client/auth/userpass/Util.java29
-rw-r--r--src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java106
-rw-r--r--src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java88
-rw-r--r--src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java44
-rw-r--r--src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java44
-rw-r--r--src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java162
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ApprovalTable.java284
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java47
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java47
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java44
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java54
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeDetailService.java33
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java94
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeListService.java98
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeManageService.java30
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java70
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeScreen.java401
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ChangeTable.java489
-rw-r--r--src/main/java/com/google/gerrit/client/changes/MessagePanel.java33
-rw-r--r--src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java38
-rw-r--r--src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java64
-rw-r--r--src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java38
-rw-r--r--src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java311
-rw-r--r--src/main/java/com/google/gerrit/client/changes/PatchSetPublishDetail.java98
-rw-r--r--src/main/java/com/google/gerrit/client/changes/PatchTable.java560
-rw-r--r--src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java325
-rw-r--r--src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java51
-rw-r--r--src/main/java/com/google/gerrit/client/changes/ToggleStarRequest.java63
-rw-r--r--src/main/java/com/google/gerrit/client/changes/Util.java57
-rw-r--r--src/main/java/com/google/gerrit/client/data/AccountDashboardInfo.java72
-rw-r--r--src/main/java/com/google/gerrit/client/data/AccountInfo.java68
-rw-r--r--src/main/java/com/google/gerrit/client/data/AccountInfoCache.java79
-rw-r--r--src/main/java/com/google/gerrit/client/data/ApprovalDetail.java85
-rw-r--r--src/main/java/com/google/gerrit/client/data/ApprovalSummary.java44
-rw-r--r--src/main/java/com/google/gerrit/client/data/ApprovalSummarySet.java49
-rw-r--r--src/main/java/com/google/gerrit/client/data/ApprovalType.java110
-rw-r--r--src/main/java/com/google/gerrit/client/data/ApprovalTypes.java67
-rw-r--r--src/main/java/com/google/gerrit/client/data/ChangeDetail.java178
-rw-r--r--src/main/java/com/google/gerrit/client/data/ChangeInfo.java92
-rw-r--r--src/main/java/com/google/gerrit/client/data/EditList.java169
-rw-r--r--src/main/java/com/google/gerrit/client/data/GerritConfig.java142
-rw-r--r--src/main/java/com/google/gerrit/client/data/GitwebLink.java84
-rw-r--r--src/main/java/com/google/gerrit/client/data/PatchScript.java96
-rw-r--r--src/main/java/com/google/gerrit/client/data/PatchScriptSettings.java68
-rw-r--r--src/main/java/com/google/gerrit/client/data/PatchSetDetail.java54
-rw-r--r--src/main/java/com/google/gerrit/client/data/ProjectInfo.java36
-rw-r--r--src/main/java/com/google/gerrit/client/data/SingleListChangeInfo.java52
-rw-r--r--src/main/java/com/google/gerrit/client/data/SparseFileContent.java153
-rw-r--r--src/main/java/com/google/gerrit/client/data/SshHostKey.java46
-rw-r--r--src/main/java/com/google/gerrit/client/data/SystemInfoService.java31
-rw-r--r--src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java609
-rw-r--r--src/main/java/com/google/gerrit/client/patches/AddReviewerResult.java80
-rw-r--r--src/main/java/com/google/gerrit/client/patches/CommentDetail.java196
-rw-r--r--src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java338
-rw-r--r--src/main/java/com/google/gerrit/client/patches/HistoryTable.java211
-rw-r--r--src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java119
-rw-r--r--src/main/java/com/google/gerrit/client/patches/PatchDetailService.java69
-rw-r--r--src/main/java/com/google/gerrit/client/patches/PatchScreen.java554
-rw-r--r--src/main/java/com/google/gerrit/client/patches/PatchUtil.java29
-rw-r--r--src/main/java/com/google/gerrit/client/patches/SideBySideTable.java289
-rw-r--r--src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java356
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AbstractAgreement.java59
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Account.java207
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountAccess.java51
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountAgreement.java118
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountAgreementAccess.java30
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountExternalId.java158
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountExternalIdAccess.java41
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGeneralPreferences.java96
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroup.java229
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAccess.java45
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreement.java114
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreementAccess.java31
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMember.java77
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAccess.java33
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAudit.java102
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAuditAccess.java32
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReview.java72
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReviewAccess.java31
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java109
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java42
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountSshKey.java153
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AccountSshKeyAccess.java33
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategory.java169
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryAccess.java34
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValue.java108
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValueAccess.java31
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/AuthType.java52
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Branch.java101
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Change.java451
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ChangeAccess.java90
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ChangeMessage.java114
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ChangeMessageAccess.java30
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ContactInformation.java85
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreement.java139
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreementAccess.java31
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Patch.java276
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchLineComment.java177
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchLineCommentAccess.java65
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSet.java159
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetAccess.java40
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestor.java88
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestorAccess.java34
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetApproval.java145
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetApprovalAccess.java50
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/PatchSetInfo.java84
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/Project.java188
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java37
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ProjectRight.java109
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ProjectRightAccess.java34
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/RevId.java57
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java135
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/SchemaVersion.java61
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/SchemaVersionAccess.java26
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/StarredChange.java69
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/StarredChangeAccess.java33
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/SystemConfig.java79
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/SystemConfigAccess.java31
-rw-r--r--src/main/java/com/google/gerrit/client/reviewdb/UserIdentity.java74
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/CodedEnum.java20
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/ContactInformationStoreException.java30
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/CorruptEntityException.java28
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/GerritCallback.java79
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/InvalidSshKeyException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/InvalidSshUserNameException.java30
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/NameAlreadyUsedException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/NoSuchAccountException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/NoSuchEntityException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/NotSignedInException.java26
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java50
-rw-r--r--src/main/java/com/google/gerrit/client/rpc/SignInRequired.java32
-rw-r--r--src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java54
-rw-r--r--src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java63
-rw-r--r--src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java63
-rw-r--r--src/main/java/com/google/gerrit/client/ui/ChangeLink.java47
-rw-r--r--src/main/java/com/google/gerrit/client/ui/CommentPanel.java193
-rw-r--r--src/main/java/com/google/gerrit/client/ui/PatchLink.java65
-rw-r--r--src/main/java/com/google/gerrit/client/ui/ProjectLink.java56
-rw-r--r--src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java63
-rw-r--r--src/main/java/com/google/gerrit/client/ui/Screen.java101
-rw-r--r--src/main/java/com/google/gerrit/client/ui/SuggestService.java34
-rw-r--r--src/main/java/com/google/gerrit/client/ui/SuggestUtil.java30
-rw-r--r--src/main/java/com/google/gerrit/git/ChangeMergeQueue.java197
-rw-r--r--src/main/java/com/google/gerrit/git/CodeReviewCommit.java73
-rw-r--r--src/main/java/com/google/gerrit/git/CommitMergeStatus.java47
-rw-r--r--src/main/java/com/google/gerrit/git/DefaultQueueOp.java29
-rw-r--r--src/main/java/com/google/gerrit/git/GitRepositoryManager.java175
-rw-r--r--src/main/java/com/google/gerrit/git/MergeException.java28
-rw-r--r--src/main/java/com/google/gerrit/git/MergeOp.java1189
-rw-r--r--src/main/java/com/google/gerrit/git/MergeQueue.java27
-rw-r--r--src/main/java/com/google/gerrit/git/MergeSorter.java92
-rw-r--r--src/main/java/com/google/gerrit/git/PatchSetImporter.java127
-rw-r--r--src/main/java/com/google/gerrit/git/PushAllProjectsOp.java91
-rw-r--r--src/main/java/com/google/gerrit/git/PushOp.java291
-rw-r--r--src/main/java/com/google/gerrit/git/PushReplication.java433
-rw-r--r--src/main/java/com/google/gerrit/git/ReloadSubmitQueueOp.java72
-rw-r--r--src/main/java/com/google/gerrit/git/ReplicationQueue.java59
-rw-r--r--src/main/java/com/google/gerrit/git/WorkQueue.java245
-rw-r--r--src/main/java/com/google/gerrit/pgm/AbstractProgram.java98
-rw-r--r--src/main/java/com/google/gerrit/pgm/CmdLineParser.java170
-rw-r--r--src/main/java/com/google/gerrit/pgm/CreateSchema.java64
-rw-r--r--src/main/java/com/google/gerrit/pgm/Daemon.java56
-rw-r--r--src/main/java/com/google/gerrit/pgm/OptionHandlerFactory.java26
-rw-r--r--src/main/java/com/google/gerrit/pgm/OptionHandlerUtil.java36
-rw-r--r--src/main/java/com/google/gerrit/pgm/Version.java62
-rw-r--r--src/main/java/com/google/gerrit/server/AnonymousUser.java48
-rw-r--r--src/main/java/com/google/gerrit/server/BaseServiceImplementation.java121
-rw-r--r--src/main/java/com/google/gerrit/server/ChangeUtil.java84
-rw-r--r--src/main/java/com/google/gerrit/server/CurrentUser.java66
-rw-r--r--src/main/java/com/google/gerrit/server/IdentifiedUser.java238
-rw-r--r--src/main/java/com/google/gerrit/server/ReplicationUser.java56
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java26
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java111
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountCache.java24
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java135
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java75
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountManager.java301
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountResolver.java86
-rw-r--r--src/main/java/com/google/gerrit/server/account/AccountState.java71
-rw-r--r--src/main/java/com/google/gerrit/server/account/AuthRequest.java107
-rw-r--r--src/main/java/com/google/gerrit/server/account/AuthResult.java43
-rw-r--r--src/main/java/com/google/gerrit/server/account/DefaultRealm.java73
-rw-r--r--src/main/java/com/google/gerrit/server/account/GroupCache.java30
-rw-r--r--src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java163
-rw-r--r--src/main/java/com/google/gerrit/server/account/GroupControl.java92
-rw-r--r--src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java36
-rw-r--r--src/main/java/com/google/gerrit/server/account/Realm.java46
-rw-r--r--src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java69
-rw-r--r--src/main/java/com/google/gerrit/server/config/AuthConfig.java193
-rw-r--r--src/main/java/com/google/gerrit/server/config/DatabaseModule.java46
-rw-r--r--src/main/java/com/google/gerrit/server/config/GerritConfigModule.java38
-rw-r--r--src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java165
-rw-r--r--src/main/java/com/google/gerrit/server/config/GerritRequestModule.java52
-rw-r--r--src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java63
-rw-r--r--src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java44
-rw-r--r--src/main/java/com/google/gerrit/server/config/SitePathProvider.java37
-rw-r--r--src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java338
-rw-r--r--src/main/java/com/google/gerrit/server/config/WildProjectName.java33
-rw-r--r--src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java45
-rw-r--r--src/main/java/com/google/gerrit/server/contact/ContactStore.java26
-rw-r--r--src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java84
-rw-r--r--src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java311
-rw-r--r--src/main/java/com/google/gerrit/server/contact/NoContactStore.java33
-rw-r--r--src/main/java/com/google/gerrit/server/http/BecomeAnyAccountLoginServlet.java173
-rw-r--r--src/main/java/com/google/gerrit/server/http/CatServlet.java326
-rw-r--r--src/main/java/com/google/gerrit/server/http/CookieBase64.java98
-rw-r--r--src/main/java/com/google/gerrit/server/http/GerritConfigProvider.java124
-rw-r--r--src/main/java/com/google/gerrit/server/http/GerritJsonServlet.java123
-rw-r--r--src/main/java/com/google/gerrit/server/http/GerritJsonServletProvider.java45
-rw-r--r--src/main/java/com/google/gerrit/server/http/GerritServletConfig.java228
-rw-r--r--src/main/java/com/google/gerrit/server/http/HostPageServlet.java248
-rw-r--r--src/main/java/com/google/gerrit/server/http/HtmlDomUtil.java246
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpAuthFilter.java113
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpAuthModule.java26
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpCanonicalWebUrlProvider.java81
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpCurrentUserProvider.java35
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpIdentifiedUserProvider.java42
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpLoginServlet.java170
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpLogoutServlet.java57
-rw-r--r--src/main/java/com/google/gerrit/server/http/HttpRemotePeerProvider.java48
-rw-r--r--src/main/java/com/google/gerrit/server/http/LegacyGerritServlet.java81
-rw-r--r--src/main/java/com/google/gerrit/server/http/PrettifyServlet.java90
-rw-r--r--src/main/java/com/google/gerrit/server/http/RequestCleanupFilter.java59
-rw-r--r--src/main/java/com/google/gerrit/server/http/RpcServletModule.java48
-rw-r--r--src/main/java/com/google/gerrit/server/http/SshServlet.java93
-rw-r--r--src/main/java/com/google/gerrit/server/http/StaticServlet.java157
-rw-r--r--src/main/java/com/google/gerrit/server/http/UrlModule.java113
-rw-r--r--src/main/java/com/google/gerrit/server/http/WebModule.java112
-rw-r--r--src/main/java/com/google/gerrit/server/http/WebSession.java177
-rw-r--r--src/main/java/com/google/gerrit/server/http/WebSessionManager.java212
-rw-r--r--src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java175
-rw-r--r--src/main/java/com/google/gerrit/server/ioutil/BlindSSLSocketFactory.java112
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LdapAuthModule.java33
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LdapModule.java44
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LdapQuery.java130
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LdapRealm.java587
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LdapType.java152
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/LoginRedirectServlet.java76
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/SearchScope.java46
-rw-r--r--src/main/java/com/google/gerrit/server/ldap/UserPassAuthServiceImpl.java67
-rw-r--r--src/main/java/com/google/gerrit/server/mail/AbandonedSender.java48
-rw-r--r--src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java38
-rw-r--r--src/main/java/com/google/gerrit/server/mail/CommentSender.java132
-rw-r--r--src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java82
-rw-r--r--src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java22
-rw-r--r--src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java136
-rw-r--r--src/main/java/com/google/gerrit/server/mail/MergeFailSender.java52
-rw-r--r--src/main/java/com/google/gerrit/server/mail/MergedSender.java178
-rw-r--r--src/main/java/com/google/gerrit/server/mail/NewChangeSender.java127
-rw-r--r--src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java632
-rw-r--r--src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java139
-rw-r--r--src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java35
-rw-r--r--src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java213
-rw-r--r--src/main/java/com/google/gerrit/server/openid/OpenIdLoginServlet.java57
-rw-r--r--src/main/java/com/google/gerrit/server/openid/OpenIdModule.java54
-rw-r--r--src/main/java/com/google/gerrit/server/openid/OpenIdServiceImpl.java468
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchFile.java115
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchList.java164
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchListCache.java28
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java200
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchListEntry.java263
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchListKey.java111
-rw-r--r--src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java117
-rw-r--r--src/main/java/com/google/gerrit/server/patch/Text.java46
-rw-r--r--src/main/java/com/google/gerrit/server/project/ChangeControl.java134
-rw-r--r--src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java28
-rw-r--r--src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java28
-rw-r--r--src/main/java/com/google/gerrit/server/project/ProjectCache.java31
-rw-r--r--src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java119
-rw-r--r--src/main/java/com/google/gerrit/server/project/ProjectControl.java227
-rw-r--r--src/main/java/com/google/gerrit/server/project/ProjectState.java107
-rw-r--r--src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java79
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/ChangeListServiceImpl.java599
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/Handler.java95
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/SuggestServiceImpl.java126
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/SystemInfoServiceImpl.java159
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/UiRpcModule.java42
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/AccountModule.java41
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/AccountSecurityImpl.java400
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/AccountServiceImpl.java172
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/AgreementInfoFactory.java80
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/ExternalIdDetailFactory.java53
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/GroupAdminServiceImpl.java363
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java111
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/account/MyGroupsFactory.java59
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/AbandonChange.java143
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailFactory.java240
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailServiceImpl.java54
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeManageServiceImpl.java43
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeModule.java41
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetDetailFactory.java127
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetPublishDetailFactory.java155
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/changedetail/SubmitAction.java158
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/AddReviewer.java158
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/CommentDetailFactory.java134
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/PatchDetailServiceImpl.java419
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/PatchModule.java39
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptBuilder.java390
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptFactory.java236
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/patch/SaveDraft.java103
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/AddBranch.java185
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/AddProjectRight.java129
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/ChangeProjectSettings.java81
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/DeleteBranches.java121
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/DeleteProjectRights.java79
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/ListBranches.java108
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/OwnedProjects.java84
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/ProjectAdminServiceImpl.java104
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/ProjectDetailFactory.java125
-rw-r--r--src/main/java/com/google/gerrit/server/rpc/project/ProjectModule.java43
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/AdminCommand.java32
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/BaseCommand.java454
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/CommandFactoryProvider.java100
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/CommandModule.java74
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/CommandName.java35
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/Commands.java139
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/DatabasePubKeyAuth.java80
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/DispatchCommand.java130
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/DispatchCommandProvider.java97
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/HostKeyProvider.java77
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/NoShell.java69
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshCurrentUserProvider.java44
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshDaemon.java515
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshInfo.java27
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java22
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshKeyCacheEntry.java65
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshKeyCacheImpl.java130
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshModule.java126
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshScopes.java117
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/SshUtil.java135
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/args4j/AccountGroupIdHandler.java57
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/args4j/AccountIdHandler.java64
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/args4j/PatchSetIdHandler.java56
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/args4j/ProjectControlHandler.java81
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AbstractGitCommand.java68
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminCreateProject.java150
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminFlushCaches.java124
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminReplicate.java87
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminShowCaches.java207
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminShowConnections.java167
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/AdminShowQueue.java133
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/ApproveCommand.java337
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/ApproveOption.java138
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/CacheCommand.java50
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/DefaultCommandModule.java54
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/ErrorSlaveMode.java40
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/ListProjects.java76
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/MasterCommandModule.java33
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/Receive.java1440
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/ScpCommand.java350
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/SlaveCommandModule.java33
-rw-r--r--src/main/java/com/google/gerrit/server/ssh/commands/Upload.java28
-rw-r--r--src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java100
-rw-r--r--src/main/java/com/google/gerrit/server/workflow/FunctionState.java261
-rw-r--r--src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java63
-rw-r--r--src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java33
-rw-r--r--src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java66
-rw-r--r--src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java204
-rw-r--r--src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java68
-rw-r--r--src/main/webapp/WEB-INF/web-jetty.xml8
-rw-r--r--src/main/webapp/WEB-INF/web.xml21
-rw-r--r--src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java362
-rw-r--r--src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java285
-rw-r--r--src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java32
-rw-r--r--src/test/java/com/google/gerrit/server/ssh/scproot/hooks/CommitMsgHookTest.java367
-rw-r--r--src/test/java/com/google/gerrit/server/ssh/scproot/hooks/HookTestCase.java102
-rw-r--r--src/test/java/com/google/gerrit/testutil/TestDatabase.java105
-rwxr-xr-xto_hosted.sh4
-rwxr-xr-xto_jetty.sh37
-rw-r--r--tools/GoogleFormat.xml (renamed from GoogleFormat.xml)0
-rw-r--r--tools/gwtui_any.launch36
-rw-r--r--tools/gwtui_mac.launch36
-rw-r--r--tools/pgm_daemon.launch20
-rwxr-xr-xtools/to_jetty.sh38
1135 files changed, 59937 insertions, 52887 deletions
diff --git a/.gitignore b/.gitignore
index b9a7f88e15..fdd9fa12a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,3 @@
-/target
-/GerritServer.properties
-/.classpath
/.project
/.settings/org.maven.ide.eclipse.prefs
-/src/main/java/GerritServer.properties
-/src/main/java/com/google/gerrit/client/GerritVersion.properties
-/src/main/java/com/google/gerrit/client/GerritVersion_*.properties
-/src/main/webapp/gerrit
-/src/main/webapp/WEB-INF/lib
-/hs_err_pid*.log
+/GerritServer.properties
diff --git a/Documentation/dev-eclipse.txt b/Documentation/dev-eclipse.txt
index 416f2a3ea9..44e85b5f6c 100644
--- a/Documentation/dev-eclipse.txt
+++ b/Documentation/dev-eclipse.txt
@@ -40,8 +40,8 @@ http://m2eclipse.codehaus.org/[m2eclipse]
Code Formatter Settings
-----------------------
-Import GoogleFormat.xml using Window -> Preferences -> Java ->
-Code Style -> Formatter -> Import...
+Import `tools/GoogleFormat.xml` using Window -> Preferences ->
+Java -> Code Style -> Formatter -> Import...
This will define the 'Google Format' profile, which the project
settings prefer when formatting source code.
@@ -59,7 +59,7 @@ builds are used.
[NOTE]
Some of the source code is generated with ANTLR sources. To
-build these files, you need to right click on the imported project,
+build these files, you need to right click on the imported projects,
Maven -> Update Project Configuration. This should fix the compile
errors identified after import.
@@ -74,19 +74,10 @@ Configure Database
You may have already done this step earlier, based on the
instructions in link:dev-readme.html[Developer Setup].
-* Open gerrit/src/main/java
-* Copy `GerritServer.properties_example` to `GerritServer.properties`
+* Navigate to gerrit-war/src/main/webapp/WEB-INF/extra/
+* Copy `GerritServer.properties_example` to `gerrit-parent/GerritServer.properties`
* Configure your database for debugging in hosted mode.
-Bootstrap Compile
------------------
-
-From the command line we have to bootstrap the environment:
-
-----
- ./to_hosted.sh
-----
-
Production Compile
------------------
@@ -96,17 +87,15 @@ Production Compile
mvn clean package
----
-to create a production build. The `./to_hosted.sh` used above
-to setup the development environment for Eclipse hosted mode also
-creates a state that produces a corrupt production build.
+to create a production build.
Launch Gerrit
-------------
Open Run->Debug Configurations...
-Under Java Application find `gerrit_debug` (not Mac OS X)
-or `gerrit_macos` (Mac OS X only) to start the GWT hosted
+Under Java Application find `gwtui_any` (not Mac OS X)
+or `gwtui_mac` (Mac OS X only) to start the GWT hosted
mode browser and debug through Eclipse.
Final Setup
diff --git a/Documentation/dev-readme.txt b/Documentation/dev-readme.txt
index e0e2017dd0..6748322ee9 100644
--- a/Documentation/dev-readme.txt
+++ b/Documentation/dev-readme.txt
@@ -43,8 +43,7 @@ gwtorm supports (or add the necessary dialect support to gwtorm,
and then configure your workspace anyway).
====
- cd src/main/java
- cp GerritServer.properties_example GerritServer.properties
+ cp gerrit-war/src/main/webapp/WEB-INF/extra/GerritServer.properties_example GerritServer.properties
====
Now edit GerritServer.properties to uncomment the database you are
@@ -104,7 +103,7 @@ From the command line:
Output WAR will be placed in:
====
- target/gerrit-*.war
+ gerrit-war/target/gerrit-*.war
====
When debugging browser specific issues use gwtStyle `DETAILED` so
diff --git a/Documentation/install.txt b/Documentation/install.txt
index fbabba1d95..1e0bd8c825 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -47,7 +47,7 @@ Maven from a source download obtained directly from Git:
git clone git://android.git.kernel.org/tools/gerrit.git
cd gerrit
mvn clean package
- cp target/gerrit-*.war ...YOUR.DEST.../gerrit.war
+ cp gerrit-war/target/gerrit-*.war ...YOUR.DEST.../gerrit.war
====
The first build may take a while as dependencies are searched
diff --git a/gerrit-common/.gitignore b/gerrit-common/.gitignore
new file mode 100644
index 0000000000..f89cac402a
--- /dev/null
+++ b/gerrit-common/.gitignore
@@ -0,0 +1,5 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
+/src/main/java/com/google/gerrit/common/Version.properties
diff --git a/.settings/org.eclipse.core.resources.prefs b/gerrit-common/.settings/org.eclipse.core.resources.prefs
index 82eb859e3b..82eb859e3b 100644
--- a/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-common/.settings/org.eclipse.core.resources.prefs
diff --git a/.settings/org.eclipse.core.runtime.prefs b/gerrit-common/.settings/org.eclipse.core.runtime.prefs
index 8667cfd4a3..8667cfd4a3 100644
--- a/.settings/org.eclipse.core.runtime.prefs
+++ b/gerrit-common/.settings/org.eclipse.core.runtime.prefs
diff --git a/.settings/org.eclipse.jdt.core.prefs b/gerrit-common/.settings/org.eclipse.jdt.core.prefs
index 04afc7fac5..04afc7fac5 100644
--- a/.settings/org.eclipse.jdt.core.prefs
+++ b/gerrit-common/.settings/org.eclipse.jdt.core.prefs
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/gerrit-common/.settings/org.eclipse.jdt.ui.prefs
index d4218a5fc0..d4218a5fc0 100644
--- a/.settings/org.eclipse.jdt.ui.prefs
+++ b/gerrit-common/.settings/org.eclipse.jdt.ui.prefs
diff --git a/gerrit-common/pom.xml b/gerrit-common/pom.xml
new file mode 100644
index 0000000000..df3c1a379f
--- /dev/null
+++ b/gerrit-common/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-common</artifactId>
+ <name>Gerrit Code Review - Common</name>
+
+ <description>
+ Classes common to both server and client.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-servlet</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-reviewdb</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-jgit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-version</id>
+ <phase>generate-resources</phase>
+ <configuration>
+ <tasks>
+ <property name="src" location="${basedir}/src/main/java/" />
+ <property name="pkg" location="${src}/com/google/gerrit/common" />
+ <exec executable="git" outputproperty="v">
+ <arg value="describe"/>
+ <arg value="HEAD"/>
+ </exec>
+ <echo file="${pkg}/Version.properties">version=${v}</echo>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/gerrit-common/src/main/java/com/google/gerrit/Common.gwt.xml b/gerrit-common/src/main/java/com/google/gerrit/Common.gwt.xml
new file mode 100644
index 0000000000..171ae8ab34
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/Common.gwt.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright (C) 2009 The Android Open Source Project
+
+ 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.
+-->
+<module>
+ <inherits name='com.google.gerrit.ReviewDB' />
+ <inherits name='com.google.gwtjsonrpc.GWTJSONRPC'/>
+ <source path='common' />
+</module>
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
new file mode 100644
index 0000000000..71b55c9217
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gwtorm.client.KeyUtil;
+
+public class PageLinks {
+ public static final String SETTINGS = "settings";
+ public static final String SETTINGS_SSHKEYS = "settings,ssh-keys";
+ public static final String SETTINGS_WEBIDENT = "settings,web-identities";
+ public static final String SETTINGS_MYGROUPS = "settings,group-memberships";
+ public static final String SETTINGS_AGREEMENTS = "settings,agreements";
+ public static final String SETTINGS_CONTACT = "settings,contact";
+ public static final String SETTINGS_PROJECTS = "settings,projects";
+ public static final String SETTINGS_NEW_AGREEMENT = "settings,new-agreement";
+ public static final String REGISTER = "register";
+
+ public static final String MINE = "mine";
+ public static final String MINE_STARRED = "mine,starred";
+ public static final String MINE_DRAFTS = "mine,drafts";
+
+ public static final String ALL_ABANDONED = "all,abandoned,n,z";
+ public static final String ALL_MERGED = "all,merged,n,z";
+ public static final String ALL_OPEN = "all,open,n,z";
+
+ public static final String ADMIN_PEOPLE = "admin,people";
+ public static final String ADMIN_GROUPS = "admin,groups";
+ public static final String ADMIN_PROJECTS = "admin,projects";
+
+ public static String toChange(final ChangeInfo c) {
+ return toChange(c.getId());
+ }
+
+ public static String toChange(final Change.Id c) {
+ return "change," + c.toString();
+ }
+
+ public static String toAccountDashboard(final AccountInfo acct) {
+ return toAccountDashboard(acct.getId());
+ }
+
+ public static String toAccountDashboard(final Account.Id acct) {
+ return "dashboard," + acct.toString();
+ }
+
+ public static String toChangeQuery(final String query) {
+ return "q," + KeyUtil.encode(query) + ",n,z";
+ }
+
+ protected PageLinks() {
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInMode.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInMode.java
new file mode 100644
index 0000000000..867ed56ca2
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInMode.java
@@ -0,0 +1,19 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth;
+
+public enum SignInMode {
+ SIGN_IN, LINK_IDENTIY, REGISTER;
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInRequired.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInRequired.java
new file mode 100644
index 0000000000..cd9fa25e92
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/SignInRequired.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth;
+
+import com.google.gerrit.common.errors.NotSignedInException;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation indicating a service method requires a current user.
+ * <p>
+ * If there is no current user then {@link NotSignedInException} will be given
+ * to the callback's onFailure method.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface SignInRequired {
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/DiscoveryResult.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/DiscoveryResult.java
new file mode 100644
index 0000000000..c5144fea4f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/DiscoveryResult.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth.openid;
+
+import java.util.Map;
+
+public final class DiscoveryResult {
+ public boolean validProvider;
+ public String providerUrl;
+ public Map<String, String> providerArgs;
+
+ protected DiscoveryResult() {
+ }
+
+ public DiscoveryResult(final boolean valid, final String redirect,
+ final Map<String, String> args) {
+ validProvider = valid;
+ providerUrl = redirect;
+ providerArgs = args;
+ }
+
+ public DiscoveryResult(final boolean fail) {
+ this(false, null, null);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdService.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdService.java
new file mode 100644
index 0000000000..48be20a5b4
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdService.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth.openid;
+
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+public interface OpenIdService extends RemoteJsonService {
+ @AllowCrossSiteRequest
+ void discover(String openidIdentifier, SignInMode mode,
+ boolean remember, String returnToken,
+ AsyncCallback<DiscoveryResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdUrls.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdUrls.java
new file mode 100644
index 0000000000..706f465a25
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/openid/OpenIdUrls.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth.openid;
+
+public class OpenIdUrls {
+ public static final String OPENID_IDENTIFIER = "openid_identifier";
+ public static final String LASTID_COOKIE = "gerrit.last_openid";
+
+ public static final String URL_YAHOO = "https://me.yahoo.com";
+ public static final String URL_GOOGLE =
+ "https://www.google.com/accounts/o8/id";
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/LoginResult.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/LoginResult.java
new file mode 100644
index 0000000000..dcabf81d70
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/LoginResult.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth.userpass;
+
+public class LoginResult {
+ public boolean success;
+ public boolean isNew;
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java
new file mode 100644
index 0000000000..955c76ac8e
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/auth/userpass/UserPassAuthService.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.auth.userpass;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+public interface UserPassAuthService extends RemoteJsonService {
+ @AllowCrossSiteRequest
+ void authenticate(String username, String password,
+ AsyncCallback<LoginResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java
new file mode 100644
index 0000000000..abcfe28bb1
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountDashboardInfo.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+
+import java.util.List;
+
+/** Summary information needed to display an account dashboard. */
+public class AccountDashboardInfo {
+ protected AccountInfoCache accounts;
+ protected Account.Id owner;
+ protected List<ChangeInfo> byOwner;
+ protected List<ChangeInfo> forReview;
+ protected List<ChangeInfo> closed;
+
+ protected AccountDashboardInfo() {
+ }
+
+ public AccountDashboardInfo(final Account.Id forUser) {
+ owner = forUser;
+ }
+
+ public AccountInfoCache getAccounts() {
+ return accounts;
+ }
+
+ public void setAccounts(final AccountInfoCache ac) {
+ accounts = ac;
+ }
+
+ public Account.Id getOwner() {
+ return owner;
+ }
+
+ public List<ChangeInfo> getByOwner() {
+ return byOwner;
+ }
+
+ public void setByOwner(List<ChangeInfo> c) {
+ byOwner = c;
+ }
+
+ public List<ChangeInfo> getForReview() {
+ return forReview;
+ }
+
+ public void setForReview(List<ChangeInfo> c) {
+ forReview = c;
+ }
+
+ public List<ChangeInfo> getClosed() {
+ return closed;
+ }
+
+ public void setClosed(List<ChangeInfo> c) {
+ closed = c;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java
new file mode 100644
index 0000000000..39c5d9c3ed
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfo.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Summary information about an {@link Account}, for simple tabular displays. */
+public class AccountInfo {
+ protected Account.Id id;
+ protected String fullName;
+ protected String preferredEmail;
+
+ protected AccountInfo() {
+ }
+
+ /**
+ * Create an 'Anonymous Coward' account info, when only the id is known.
+ * <p>
+ * This constructor should only be a last-ditch effort, when the usual account
+ * lookup has failed and a stale account id has been discovered in the data
+ * store.
+ */
+ public AccountInfo(final Account.Id id) {
+ this.id = id;
+ }
+
+ /**
+ * Create an account description from a real data store record.
+ *
+ * @param a the data store record holding the specific account details.
+ */
+ public AccountInfo(final Account a) {
+ id = a.getId();
+ fullName = a.getFullName();
+ preferredEmail = a.getPreferredEmail();
+ }
+
+ /** @return the unique local id of the account */
+ public Account.Id getId() {
+ return id;
+ }
+
+ /** @return the full name of the account holder; null if not supplied */
+ public String getFullName() {
+ return fullName;
+ }
+
+ /** @return the email address of the account holder; null if not supplied */
+ public String getPreferredEmail() {
+ return preferredEmail;
+ }
+
+ public void setPreferredEmail(final String email) {
+ preferredEmail = email;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfoCache.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfoCache.java
new file mode 100644
index 0000000000..cf5f20b56e
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountInfoCache.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** In-memory table of {@link AccountInfo}, indexed by {@link Account.Id}. */
+public class AccountInfoCache {
+ private static final AccountInfoCache EMPTY;
+ static {
+ EMPTY = new AccountInfoCache();
+ EMPTY.accounts = Collections.emptyMap();
+ }
+
+ /** Obtain an empty cache singleton. */
+ public static AccountInfoCache empty() {
+ return EMPTY;
+ }
+
+ protected Map<Account.Id, AccountInfo> accounts;
+
+ protected AccountInfoCache() {
+ }
+
+ public AccountInfoCache(final Iterable<AccountInfo> list) {
+ accounts = new HashMap<Account.Id, AccountInfo>();
+ for (final AccountInfo ai : list) {
+ accounts.put(ai.getId(), ai);
+ }
+ }
+
+ /**
+ * Lookup the account summary
+ * <p>
+ * The return value can take on one of three forms:
+ * <ul>
+ * <li><code>null</code>, if <code>id == null</code>.</li>
+ * <li>a valid info block, if <code>id</code> was loaded.</li>
+ * <li>an anonymous info block, if <code>id</code> was not loaded.</li>
+ * </ul>
+ *
+ * @param id the id desired.
+ * @return info block for the account.
+ */
+ public AccountInfo get(final Account.Id id) {
+ if (id == null) {
+ return null;
+ }
+
+ AccountInfo r = accounts.get(id);
+ if (r == null) {
+ r = new AccountInfo(id);
+ accounts.put(id, r);
+ }
+ return r;
+ }
+
+ /** Merge the information from another cache into this one. */
+ public void merge(final AccountInfoCache other) {
+ assert this != EMPTY;
+ accounts.putAll(other.accounts);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountProjectWatchInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountProjectWatchInfo.java
new file mode 100644
index 0000000000..89a50f125e
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountProjectWatchInfo.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.Project;
+
+public final class AccountProjectWatchInfo {
+ protected AccountProjectWatch watch;
+ protected Project project;
+
+ protected AccountProjectWatchInfo() {
+ }
+
+ public AccountProjectWatchInfo(final AccountProjectWatch w, final Project p) {
+ watch = w;
+ project = p;
+ }
+
+ public AccountProjectWatch getWatch() {
+ return watch;
+ }
+
+ public Project getProject() {
+ return project;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
new file mode 100644
index 0000000000..804dab8c92
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountSecurity.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gerrit.reviewdb.ContactInformation;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface AccountSecurity extends RemoteJsonService {
+ @SignInRequired
+ void mySshKeys(AsyncCallback<List<AccountSshKey>> callback);
+
+ @SignInRequired
+ void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
+
+ @SignInRequired
+ void deleteSshKeys(Set<AccountSshKey.Id> ids,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void changeSshUserName(String newName, AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
+
+ @SignInRequired
+ void myGroups(AsyncCallback<List<AccountGroup>> callback);
+
+ @SignInRequired
+ void deleteExternalIds(Set<AccountExternalId.Key> keys,
+ AsyncCallback<Set<AccountExternalId.Key>> callback);
+
+ @SignInRequired
+ void updateContact(String fullName, String emailAddr,
+ ContactInformation info, AsyncCallback<Account> callback);
+
+ @SignInRequired
+ void enterAgreement(ContributorAgreement.Id id,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void registerEmail(String address, AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void validateEmail(String token, AsyncCallback<VoidResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
new file mode 100644
index 0000000000..eaf84fa4a1
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccountService.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface AccountService extends RemoteJsonService {
+ @SignInRequired
+ void myAccount(AsyncCallback<Account> callback);
+
+ @SignInRequired
+ void changePreferences(AccountGeneralPreferences pref,
+ AsyncCallback<VoidResult> gerritCallback);
+
+ @SignInRequired
+ void myProjectWatch(AsyncCallback<List<AccountProjectWatchInfo>> callback);
+
+ @SignInRequired
+ void addProjectWatch(String projectName,
+ AsyncCallback<AccountProjectWatchInfo> callback);
+
+ @SignInRequired
+ void updateProjectWatch(AccountProjectWatch watch,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void deleteProjectWatches(Set<AccountProjectWatch.Key> keys,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void myAgreements(AsyncCallback<AgreementInfo> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AddReviewerResult.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AddReviewerResult.java
new file mode 100644
index 0000000000..e8764a7ef2
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AddReviewerResult.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Result from adding a reviewer to a change. */
+public class AddReviewerResult {
+ protected List<Error> errors;
+ protected ChangeDetail change;
+
+ public AddReviewerResult() {
+ errors = new ArrayList<Error>();
+ }
+
+ public void addError(final Error e) {
+ errors.add(e);
+ }
+
+ public List<Error> getErrors() {
+ return errors;
+ }
+
+ public ChangeDetail getChange() {
+ return change;
+ }
+
+ public void setChange(final ChangeDetail d) {
+ change = d;
+ }
+
+ public static class Error {
+ public static enum Type {
+ /** Name supplied does not match to a registered account. */
+ ACCOUNT_NOT_FOUND,
+
+ /** The account is not permitted to see the change. */
+ CHANGE_NOT_VISIBLE
+ }
+
+ protected Type type;
+ protected String name;
+
+ protected Error() {
+ }
+
+ public Error(final Type type, final String who) {
+ this.type = type;
+ this.name = who;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return type + " " + name;
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java
new file mode 100644
index 0000000000..7ae651ee62
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AgreementInfo.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountGroupAgreement;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+
+import java.util.List;
+import java.util.Map;
+
+public class AgreementInfo {
+ public List<AccountAgreement> userAccepted;
+ public List<AccountGroupAgreement> groupAccepted;
+ public Map<ContributorAgreement.Id, ContributorAgreement> agreements;
+
+ public AgreementInfo() {
+ }
+
+ public void setUserAccepted(List<AccountAgreement> a) {
+ userAccepted = a;
+ }
+
+ public void setGroupAccepted(List<AccountGroupAgreement> a) {
+ groupAccepted = a;
+ }
+
+ public void setAgreements(Map<ContributorAgreement.Id, ContributorAgreement> a) {
+ agreements = a;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalDetail.java
new file mode 100644
index 0000000000..33f4da741f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalDetail.java
@@ -0,0 +1,85 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ApprovalDetail {
+ public static final Comparator<ApprovalDetail> SORT =
+ new Comparator<ApprovalDetail>() {
+ public int compare(final ApprovalDetail o1, final ApprovalDetail o2) {
+ int cmp;
+ cmp = o2.hasNonZero - o1.hasNonZero;
+ if (cmp != 0) return cmp;
+ return o1.sortOrder.compareTo(o2.sortOrder);
+ }
+ };
+
+ static final Timestamp EG_0 = new Timestamp(0);
+ static final Timestamp EG_D = new Timestamp(Long.MAX_VALUE);
+
+ protected Account.Id account;
+ protected List<PatchSetApproval> approvals;
+
+ private transient int hasNonZero;
+ private transient Timestamp sortOrder = EG_D;
+
+ protected ApprovalDetail() {
+ }
+
+ public ApprovalDetail(final Account.Id id) {
+ account = id;
+ approvals = new ArrayList<PatchSetApproval>();
+ }
+
+ public Account.Id getAccount() {
+ return account;
+ }
+
+ public Map<ApprovalCategory.Id, PatchSetApproval> getApprovalMap() {
+ final HashMap<ApprovalCategory.Id, PatchSetApproval> r;
+ r = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ for (final PatchSetApproval ca : approvals) {
+ r.put(ca.getCategoryId(), ca);
+ }
+ return r;
+ }
+
+ public void sortFirst() {
+ hasNonZero = 1;
+ sortOrder = ApprovalDetail.EG_0;
+ }
+
+ public void add(final PatchSetApproval ca) {
+ approvals.add(ca);
+
+ final Timestamp g = ca.getGranted();
+ if (g != null && g.compareTo(sortOrder) < 0) {
+ sortOrder = g;
+ }
+ if (ca.getValue() != 0) {
+ hasNonZero = 1;
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummary.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummary.java
new file mode 100644
index 0000000000..57e8b71d10
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummary.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Summarizes the approvals (or negative approvals) for a patch set.
+ * This will typically contain zero or one approvals for each
+ * category, with all of the approvals coming from a single patch set.
+ */
+public class ApprovalSummary {
+ protected Map<ApprovalCategory.Id, PatchSetApproval> approvals;
+
+ protected ApprovalSummary() {
+ }
+
+ public ApprovalSummary(final Iterable<PatchSetApproval> list) {
+ approvals = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ for (final PatchSetApproval a : list) {
+ approvals.put(a.getCategoryId(), a);
+ }
+ }
+
+ public Map<ApprovalCategory.Id, PatchSetApproval> getApprovalMap() {
+ return Collections.unmodifiableMap(approvals);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummarySet.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummarySet.java
new file mode 100644
index 0000000000..57ee92873f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalSummarySet.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Change;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Contains a set of ApprovalSummary objects, keyed by the change id
+ * from which they were derived.
+ */
+public class ApprovalSummarySet {
+ protected AccountInfoCache accounts;
+
+ protected Map<Change.Id, ApprovalSummary> summaries;
+
+ protected ApprovalSummarySet() {
+ }
+
+ public ApprovalSummarySet(final AccountInfoCache accts,
+ final Map<Change.Id, ApprovalSummary> map) {
+ accounts = accts;
+
+ summaries = new HashMap<Change.Id, ApprovalSummary>();
+ summaries.putAll(map);
+ }
+
+ public AccountInfoCache getAccountInfoCache() {
+ return accounts;
+ }
+
+ public Map<Change.Id, ApprovalSummary> getSummaryMap() {
+ return Collections.unmodifiableMap(summaries);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
new file mode 100644
index 0000000000..ea9aedb750
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
@@ -0,0 +1,110 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ApprovalType {
+ protected ApprovalCategory category;
+ protected List<ApprovalCategoryValue> values;
+ protected short maxNegative;
+ protected short maxPositive;
+
+ private transient Map<Short, ApprovalCategoryValue> byValue;
+
+ protected ApprovalType() {
+ }
+
+ public ApprovalType(final ApprovalCategory ac,
+ final List<ApprovalCategoryValue> valueList) {
+ category = ac;
+ values = new ArrayList<ApprovalCategoryValue>(valueList);
+ Collections.sort(values, new Comparator<ApprovalCategoryValue>() {
+ public int compare(ApprovalCategoryValue o1, ApprovalCategoryValue o2) {
+ return o1.getValue() - o2.getValue();
+ }
+ });
+
+ maxNegative = Short.MIN_VALUE;
+ maxPositive = Short.MAX_VALUE;
+ if (values.size() > 0) {
+ if (values.get(0).getValue() < 0) {
+ maxNegative = values.get(0).getValue();
+ }
+ if (values.get(values.size() - 1).getValue() > 0) {
+ maxPositive = values.get(values.size() - 1).getValue();
+ }
+ }
+ }
+
+ public ApprovalCategory getCategory() {
+ return category;
+ }
+
+ public List<ApprovalCategoryValue> getValues() {
+ return values;
+ }
+
+ public ApprovalCategoryValue getMin() {
+ if (values.isEmpty()) {
+ return null;
+ }
+ return values.get(0);
+ }
+
+ public ApprovalCategoryValue getMax() {
+ if (values.isEmpty()) {
+ return null;
+ }
+ final ApprovalCategoryValue v = values.get(values.size() - 1);
+ return v.getValue() > 0 ? v : null;
+ }
+
+ public boolean isMaxNegative(final PatchSetApproval ca) {
+ return maxNegative == ca.getValue();
+ }
+
+ public boolean isMaxPositive(final PatchSetApproval ca) {
+ return maxPositive == ca.getValue();
+ }
+
+ public ApprovalCategoryValue getValue(final short value) {
+ initByValue();
+ return byValue.get(value);
+ }
+
+ public ApprovalCategoryValue getValue(final PatchSetApproval ca) {
+ initByValue();
+ return byValue.get(ca.getValue());
+ }
+
+ private void initByValue() {
+ if (byValue == null) {
+ byValue = new HashMap<Short, ApprovalCategoryValue>();
+ for (final ApprovalCategoryValue acv : values) {
+ byValue.put(acv.getValue(), acv);
+ }
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
new file mode 100644
index 0000000000..01dca1c966
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.ApprovalCategory;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ApprovalTypes {
+ protected List<ApprovalType> approvalTypes;
+ protected List<ApprovalType> actionTypes;
+ private transient Map<ApprovalCategory.Id, ApprovalType> byCategoryId;
+
+ protected ApprovalTypes() {
+ }
+
+ public ApprovalTypes(final List<ApprovalType> approvals,
+ final List<ApprovalType> actions) {
+ approvalTypes = approvals;
+ actionTypes = actions;
+ byCategory();
+ }
+
+ public List<ApprovalType> getApprovalTypes() {
+ return approvalTypes;
+ }
+
+ public List<ApprovalType> getActionTypes() {
+ return actionTypes;
+ }
+
+ public ApprovalType getApprovalType(final ApprovalCategory.Id id) {
+ return byCategory().get(id);
+ }
+
+ private Map<ApprovalCategory.Id, ApprovalType> byCategory() {
+ if (byCategoryId == null) {
+ byCategoryId = new HashMap<ApprovalCategory.Id, ApprovalType>();
+ if (actionTypes != null) {
+ for (final ApprovalType t : actionTypes) {
+ byCategoryId.put(t.getCategory().getId(), t);
+ }
+ }
+
+ if (approvalTypes != null) {
+ for (final ApprovalType t : approvalTypes) {
+ byCategoryId.put(t.getCategory().getId(), t);
+ }
+ }
+ }
+ return byCategoryId;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
new file mode 100644
index 0000000000..80c1c31f28
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -0,0 +1,177 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/** Detail necessary to display a change. */
+public class ChangeDetail {
+ protected AccountInfoCache accounts;
+ protected boolean allowsAnonymous;
+ protected boolean canAbandon;
+ protected Change change;
+ protected boolean starred;
+ protected List<ChangeInfo> dependsOn;
+ protected List<ChangeInfo> neededBy;
+ protected List<PatchSet> patchSets;
+ protected List<ApprovalDetail> approvals;
+ protected Set<ApprovalCategory.Id> missingApprovals;
+ protected List<ChangeMessage> messages;
+ protected PatchSet.Id currentPatchSetId;
+ protected PatchSetDetail currentDetail;
+ protected Set<ApprovalCategory.Id> currentActions;
+
+ public ChangeDetail() {
+ }
+
+ public AccountInfoCache getAccounts() {
+ return accounts;
+ }
+
+ public void setAccounts(AccountInfoCache aic) {
+ accounts = aic;
+ }
+
+ public boolean isAllowsAnonymous() {
+ return allowsAnonymous;
+ }
+
+ public void setAllowsAnonymous(final boolean anon) {
+ allowsAnonymous = anon;
+ }
+
+ public boolean canAbandon() {
+ return canAbandon;
+ }
+
+ public void setCanAbandon(final boolean a) {
+ canAbandon = a;
+ }
+
+ public Change getChange() {
+ return change;
+ }
+
+ public void setChange(final Change change) {
+ this.change = change;
+ this.currentPatchSetId = change.currentPatchSetId();
+ }
+
+ public boolean isStarred() {
+ return starred;
+ }
+
+ public void setStarred(final boolean s) {
+ starred = s;
+ }
+
+ public List<ChangeInfo> getDependsOn() {
+ return dependsOn;
+ }
+
+ public void setDependsOn(List<ChangeInfo> d) {
+ dependsOn = d;
+ }
+
+ public List<ChangeInfo> getNeededBy() {
+ return neededBy;
+ }
+
+ public void setNeededBy(List<ChangeInfo> d) {
+ neededBy = d;
+ }
+
+ public List<ChangeMessage> getMessages() {
+ return messages;
+ }
+
+ public void setMessages(List<ChangeMessage> m) {
+ messages = m;
+ }
+
+ public List<PatchSet> getPatchSets() {
+ return patchSets;
+ }
+
+ public void setPatchSets(List<PatchSet> s) {
+ patchSets = s;
+ }
+
+ public List<ApprovalDetail> getApprovals() {
+ return approvals;
+ }
+
+ public void setApprovals(Collection<ApprovalDetail> list) {
+ approvals = new ArrayList<ApprovalDetail>(list);
+ Collections.sort(approvals, ApprovalDetail.SORT);
+ }
+
+ public Set<ApprovalCategory.Id> getMissingApprovals() {
+ return missingApprovals;
+ }
+
+ public void setMissingApprovals(Set<ApprovalCategory.Id> a) {
+ missingApprovals = a;
+ }
+
+ public Set<ApprovalCategory.Id> getCurrentActions() {
+ return currentActions;
+ }
+
+ public void setCurrentActions(Set<ApprovalCategory.Id> a) {
+ currentActions = a;
+ }
+
+ public boolean isCurrentPatchSet(final PatchSetDetail detail) {
+ return currentPatchSetId != null
+ && detail.getPatchSet().getId().equals(currentPatchSetId);
+ }
+
+ public PatchSet getCurrentPatchSet() {
+ if (currentPatchSetId != null) {
+ // We search through the list backwards because its *very* likely
+ // that the current patch set is also the last patch set.
+ //
+ for (int i = patchSets.size() - 1; i >= 0; i--) {
+ final PatchSet ps = patchSets.get(i);
+ if (ps.getId().equals(currentPatchSetId)) {
+ return ps;
+ }
+ }
+ }
+ return null;
+ }
+
+ public PatchSetDetail getCurrentPatchSetDetail() {
+ return currentDetail;
+ }
+
+ public void setCurrentPatchSetDetail(PatchSetDetail d) {
+ currentDetail = d;
+ }
+
+ public String getDescription() {
+ return currentDetail != null ? currentDetail.getInfo().getMessage() : "";
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
new file mode 100644
index 0000000000..e79895ae6d
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetailService.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+public interface ChangeDetailService extends RemoteJsonService {
+ void changeDetail(Change.Id id, AsyncCallback<ChangeDetail> callback);
+
+ void patchSetDetail(PatchSet.Id key, AsyncCallback<PatchSetDetail> callback);
+
+ @SignInRequired
+ void patchSetPublishDetail(PatchSet.Id key,
+ AsyncCallback<PatchSetPublishDetail> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java
new file mode 100644
index 0000000000..3de4ee090f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeInfo.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+
+import java.sql.Timestamp;
+
+public class ChangeInfo {
+ protected Change.Id id;
+ protected Change.Key key;
+ protected Account.Id owner;
+ protected String subject;
+ protected Change.Status status;
+ protected ProjectInfo project;
+ protected String branch;
+ protected boolean starred;
+ protected Timestamp lastUpdatedOn;
+ protected String sortKey;
+
+ protected ChangeInfo() {
+ }
+
+ public ChangeInfo(final Change c) {
+ id = c.getId();
+ key = c.getKey();
+ owner = c.getOwner();
+ subject = c.getSubject();
+ status = c.getStatus();
+ project = new ProjectInfo(c.getProject());
+ branch = c.getDest().getShortName();
+ lastUpdatedOn = c.getLastUpdatedOn();
+ sortKey = c.getSortKey();
+ }
+
+ public Change.Id getId() {
+ return id;
+ }
+
+ public Change.Key getKey() {
+ return key;
+ }
+
+ public Account.Id getOwner() {
+ return owner;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public Change.Status getStatus() {
+ return status;
+ }
+
+ public ProjectInfo getProject() {
+ return project;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public boolean isStarred() {
+ return starred;
+ }
+
+ public void setStarred(final boolean s) {
+ starred = s;
+ }
+
+ public java.sql.Timestamp getLastUpdatedOn() {
+ return lastUpdatedOn;
+ }
+
+ public String getSortKey() {
+ return sortKey;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
new file mode 100644
index 0000000000..c9668ca7e0
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.Set;
+
+public interface ChangeListService extends RemoteJsonService {
+ /** Get all open changes more recent than pos, fetching at most limit rows. */
+ void allOpenPrev(String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all open changes older than pos, fetching at most limit rows. */
+ void allOpenNext(String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all open changes more recent than pos, fetching at most limit rows. */
+ void byProjectOpenPrev(Project.NameKey project, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all open changes older than pos, fetching at most limit rows. */
+ void byProjectOpenNext(Project.NameKey project, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /**
+ * Get all closed changes with same status, more recent than pos, fetching at
+ * most limit rows.
+ */
+ void byProjectClosedPrev(Project.NameKey project, Change.Status status,
+ String pos, int limit, AsyncCallback<SingleListChangeInfo> callback);
+
+ /**
+ * Get all closed changes with same status, older than pos, fetching at most
+ * limit rows.
+ */
+ void byProjectClosedNext(Project.NameKey project, Change.Status status,
+ String pos, int limit, AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all closed changes more recent than pos, fetching at most limit rows. */
+ void allClosedPrev(Change.Status status, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all closed changes older than pos, fetching at most limit rows. */
+ void allClosedNext(Change.Status status, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all changes which match an arbitrary query string. */
+ void allQueryPrev(String query, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get all changes which match an arbitrary query string. */
+ void allQueryNext(String query, String pos, int limit,
+ AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get the data to show AccountDashboardScreen for an account. */
+ void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
+
+ /** Get the changes starred by the caller. */
+ @SignInRequired
+ void myStarredChanges(AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get the changes with unpublished drafts by the caller. */
+ @SignInRequired
+ void myDraftChanges(AsyncCallback<SingleListChangeInfo> callback);
+
+ /** Get the ids of all changes starred by the caller. */
+ @SignInRequired
+ void myStarredChangeIds(AsyncCallback<Set<Change.Id>> callback);
+
+ /**
+ * Add and/or remove changes from the set of starred changes of the caller.
+ *
+ * @param req the add and remove cluster.
+ */
+ @SignInRequired
+ void toggleStars(ToggleStarRequest req, AsyncCallback<VoidResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
new file mode 100644
index 0000000000..dc94557468
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeManageService.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+public interface ChangeManageService extends RemoteJsonService {
+ @SignInRequired
+ void submit(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
+
+ @SignInRequired
+ void abandonChange(PatchSet.Id patchSetId, String message,
+ AsyncCallback<ChangeDetail> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
new file mode 100644
index 0000000000..e35fb78c5f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/CommentDetail.java
@@ -0,0 +1,195 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CommentDetail {
+ protected List<PatchLineComment> commentsA;
+ protected List<PatchLineComment> commentsB;
+ protected List<Patch> history;
+ protected AccountInfoCache accounts;
+
+ private transient PatchSet.Id idA;
+ private transient PatchSet.Id idB;
+ private transient Map<Integer, List<PatchLineComment>> forA;
+ private transient Map<Integer, List<PatchLineComment>> forB;
+
+ public CommentDetail(final PatchSet.Id a, final PatchSet.Id b) {
+ commentsA = new ArrayList<PatchLineComment>();
+ commentsB = new ArrayList<PatchLineComment>();
+
+ idA = a;
+ idB = b;
+ }
+
+ protected CommentDetail() {
+ }
+
+ public boolean include(final PatchLineComment p) {
+ final PatchSet.Id psId = p.getKey().getParentKey().getParentKey();
+ switch (p.getSide()) {
+ case 0:
+ if (idA == null && idB.equals(psId)) {
+ commentsA.add(p);
+ return true;
+ }
+ break;
+
+ case 1:
+ if (idA != null && idA.equals(psId)) {
+ commentsA.add(p);
+ return true;
+ }
+
+ if (idB.equals(psId)) {
+ commentsB.add(p);
+ return true;
+ }
+ break;
+ }
+ return false;
+ }
+
+ public void setAccountInfoCache(final AccountInfoCache a) {
+ accounts = a;
+ }
+
+ public void setHistory(final List<Patch> h) {
+ history = h;
+ }
+
+ public AccountInfoCache getAccounts() {
+ return accounts;
+ }
+
+ public List<Patch> getHistory() {
+ return history;
+ }
+
+ public List<PatchLineComment> getCommentsA() {
+ return commentsA;
+ }
+
+ public List<PatchLineComment> getCommentsB() {
+ return commentsB;
+ }
+
+ public boolean isEmpty() {
+ return commentsA.isEmpty() && commentsB.isEmpty();
+ }
+
+ public List<PatchLineComment> getForA(final int lineNbr) {
+ if (lineNbr == 0) {
+ return Collections.emptyList();
+ }
+ if (forA == null) {
+ forA = index(commentsA);
+ }
+ return get(forA, lineNbr);
+ }
+
+ public List<PatchLineComment> getForB(final int lineNbr) {
+ if (lineNbr == 0) {
+ return Collections.emptyList();
+ }
+ if (forB == null) {
+ forB = index(commentsB);
+ }
+ return get(forB, lineNbr);
+ }
+
+ private static List<PatchLineComment> get(
+ final Map<Integer, List<PatchLineComment>> m, final int i) {
+ final List<PatchLineComment> r = m.get(i);
+ return r != null ? orderComments(r) : Collections.<PatchLineComment> emptyList();
+ }
+
+ /**
+ * Order the comments based on their parent_uuid parent. It is possible to do this by
+ * iterating over the list only once but it's probably overkill since the number of comments
+ * on a given line will be small most of the time.
+ *
+ * @param comments The list of comments for a given line.
+ * @return The comments sorted as they should appear in the UI
+ */
+ private static List<PatchLineComment> orderComments(List<PatchLineComment> comments) {
+ // Map of comments keyed by their parent. The values are lists of comments since it is
+ // possible for several comments to have the same parent (this can happen if two reviewers
+ // click Reply on the same comment at the same time). Such comments will be displayed under
+ // their correct parent in chronological order.
+ Map<String, List<PatchLineComment>> parentMap = new HashMap<String, List<PatchLineComment>>();
+
+ // It's possible to have more than one root comment if two reviewers create a comment on the
+ // same line at the same time
+ List<PatchLineComment> rootComments = new ArrayList<PatchLineComment>();
+
+ // Store all the comments in parentMap, keyed by their parent
+ for (PatchLineComment c : comments) {
+ String parentUuid = c.getParentUuid();
+ List<PatchLineComment> l = parentMap.get(parentUuid);
+ if (l == null) {
+ l = new ArrayList<PatchLineComment>();
+ parentMap.put(parentUuid, l);
+ }
+ l.add(c);
+ if (parentUuid == null) rootComments.add(c);
+ }
+
+ // Add the comments in the list, starting with the head and then going through all the
+ // comments that have it as a parent, and so on
+ List<PatchLineComment> result = new ArrayList<PatchLineComment>();
+ addChildren(parentMap, rootComments, result);
+
+ return result;
+ }
+
+ /**
+ * Add the comments to <code>outResult</code>, depth first
+ */
+ private static void addChildren(Map<String, List<PatchLineComment>> parentMap,
+ List<PatchLineComment> children, List<PatchLineComment> outResult) {
+ if (children != null) {
+ for (PatchLineComment c : children) {
+ outResult.add(c);
+ addChildren(parentMap, parentMap.get(c.getKey().get()), outResult);
+ }
+ }
+ }
+
+ private Map<Integer, List<PatchLineComment>> index(
+ final List<PatchLineComment> in) {
+ final HashMap<Integer, List<PatchLineComment>> r;
+
+ r = new HashMap<Integer, List<PatchLineComment>>();
+ for (final PatchLineComment p : in) {
+ List<PatchLineComment> l = r.get(p.getLine());
+ if (l == null) {
+ l = new ArrayList<PatchLineComment>();
+ r.put(p.getLine(), l);
+ }
+ l.add(p);
+ }
+ return r;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/EditList.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/EditList.java
new file mode 100644
index 0000000000..5dd55eb019
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/EditList.java
@@ -0,0 +1,169 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import org.eclipse.jgit.diff.Edit;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class EditList {
+ private final List<Edit> edits;
+ private final int context;
+ private final int aSize;
+ private final int bSize;
+
+ public EditList(final List<Edit> edits, final int contextLines,
+ final int aSize, final int bSize) {
+ this.edits = edits;
+ this.context = contextLines;
+ this.aSize = aSize;
+ this.bSize = bSize;
+ }
+
+ public List<Edit> getEdits() {
+ return edits;
+ }
+
+ public Iterable<Hunk> getHunks() {
+ return new Iterable<Hunk>() {
+ public Iterator<Hunk> iterator() {
+ return new Iterator<Hunk>() {
+ private int curIdx;
+
+ public boolean hasNext() {
+ return curIdx < edits.size();
+ }
+
+ public Hunk next() {
+ final int c = curIdx;
+ final int e = findCombinedEnd(c);
+ curIdx = e + 1;
+ return new Hunk(c, e);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ private int findCombinedEnd(final int i) {
+ int end = i + 1;
+ while (end < edits.size() && (combineA(end) || combineB(end)))
+ end++;
+ return end - 1;
+ }
+
+ private boolean combineA(final int i) {
+ final Edit s = edits.get(i);
+ final Edit e = edits.get(i - 1);
+ return s.getBeginA() - e.getEndA() <= 2 * context;
+ }
+
+ private boolean combineB(final int i) {
+ final int s = edits.get(i).getBeginB();
+ final int e = edits.get(i - 1).getEndB();
+ return s - e <= 2 * context;
+ }
+
+ public class Hunk {
+ private int curIdx;
+ private Edit curEdit;
+ private final int endIdx;
+ private final Edit endEdit;
+
+ private int aCur;
+ private int bCur;
+ private final int aEnd;
+ private final int bEnd;
+
+ private Hunk(final int ci, final int ei) {
+ curIdx = ci;
+ endIdx = ei;
+ curEdit = edits.get(curIdx);
+ endEdit = edits.get(endIdx);
+
+ aCur = Math.max(0, curEdit.getBeginA() - context);
+ bCur = Math.max(0, curEdit.getBeginB() - context);
+ aEnd = Math.min(aSize, endEdit.getEndA() + context);
+ bEnd = Math.min(bSize, endEdit.getEndB() + context);
+ }
+
+ public int getCurA() {
+ return aCur;
+ }
+
+ public int getCurB() {
+ return bCur;
+ }
+
+ public int getEndA() {
+ return aEnd;
+ }
+
+ public int getEndB() {
+ return bEnd;
+ }
+
+ public void incA() {
+ aCur++;
+ }
+
+ public void incB() {
+ bCur++;
+ }
+
+ public void incBoth() {
+ incA();
+ incB();
+ }
+
+ public boolean isStartOfFile() {
+ return aCur == 0 && bCur == 0;
+ }
+
+ public boolean isContextLine() {
+ return !isModifiedLine();
+ }
+
+ public boolean isDeletedA() {
+ return curEdit.getBeginA() <= aCur && aCur < curEdit.getEndA();
+ }
+
+ public boolean isInsertedB() {
+ return curEdit.getBeginB() <= bCur && bCur < curEdit.getEndB();
+ }
+
+ public boolean isModifiedLine() {
+ return isDeletedA() || isInsertedB();
+ }
+
+ public boolean next() {
+ if (!in(curEdit)) {
+ if (curIdx < endIdx) {
+ curEdit = edits.get(++curIdx);
+ }
+ }
+ return aCur < aEnd || bCur < bEnd;
+ }
+
+ private boolean in(final Edit edit) {
+ return aCur < edit.getEndA() || bCur < edit.getEndB();
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
new file mode 100644
index 0000000000..b664b6506f
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -0,0 +1,142 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwtexpui.safehtml.client.RegexFindReplace;
+
+import java.util.List;
+import java.util.Set;
+
+public class GerritConfig implements Cloneable {
+ protected String canonicalUrl;
+ protected GitwebLink gitweb;
+ protected boolean useContributorAgreements;
+ protected boolean useContactInfo;
+ protected boolean allowRegisterNewEmail;
+ protected AuthType authType;
+ protected boolean useRepoDownload;
+ protected String gitDaemonUrl;
+ protected String sshdAddress;
+ protected Project.NameKey wildProject;
+ protected ApprovalTypes approvalTypes;
+ protected Set<Account.FieldName> editableAccountFields;
+ protected List<RegexFindReplace> commentLinks;
+
+ public String getCanonicalUrl() {
+ return canonicalUrl;
+ }
+
+ public void setCanonicalUrl(final String u) {
+ canonicalUrl = u;
+ }
+
+ public AuthType getAuthType() {
+ return authType;
+ }
+
+ public void setAuthType(final AuthType t) {
+ authType = t;
+ }
+
+ public GitwebLink getGitwebLink() {
+ return gitweb;
+ }
+
+ public void setGitwebLink(final GitwebLink w) {
+ gitweb = w;
+ }
+
+ public boolean isUseContributorAgreements() {
+ return useContributorAgreements;
+ }
+
+ public void setUseContributorAgreements(final boolean r) {
+ useContributorAgreements = r;
+ }
+
+ public boolean isUseContactInfo() {
+ return useContactInfo;
+ }
+
+ public void setUseContactInfo(final boolean r) {
+ useContactInfo = r;
+ }
+
+ public boolean isUseRepoDownload() {
+ return useRepoDownload;
+ }
+
+ public void setUseRepoDownload(final boolean r) {
+ useRepoDownload = r;
+ }
+
+ public String getGitDaemonUrl() {
+ return gitDaemonUrl;
+ }
+
+ public void setGitDaemonUrl(String url) {
+ if (url != null && !url.endsWith("/")) {
+ url += "/";
+ }
+ gitDaemonUrl = url;
+ }
+
+ public String getSshdAddress() {
+ return sshdAddress;
+ }
+
+ public void setSshdAddress(final String addr) {
+ sshdAddress = addr;
+ }
+
+ public Project.NameKey getWildProject() {
+ return wildProject;
+ }
+
+ public void setWildProject(final Project.NameKey wp) {
+ wildProject = wp;
+ }
+
+ public ApprovalTypes getApprovalTypes() {
+ return approvalTypes;
+ }
+
+ public void setApprovalTypes(final ApprovalTypes at) {
+ approvalTypes = at;
+ }
+
+ public boolean canEdit(final Account.FieldName f) {
+ return editableAccountFields.contains(f);
+ }
+
+ public Set<Account.FieldName> getEditableAccountFields() {
+ return editableAccountFields;
+ }
+
+ public void setEditableAccountFields(final Set<Account.FieldName> af) {
+ editableAccountFields = af;
+ }
+
+ public List<RegexFindReplace> getCommentLinks() {
+ return commentLinks;
+ }
+
+ public void setCommentLinks(final List<RegexFindReplace> cl) {
+ commentLinks = cl;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java
new file mode 100644
index 0000000000..31963c1664
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.http.client.URL;
+
+/** Link to an external gitweb server. */
+public class GitwebLink {
+ protected String baseUrl;
+
+ protected GitwebLink() {
+ }
+
+ public GitwebLink(final String base) {
+ baseUrl = base + "?";
+ }
+
+ public String toRevision(final Project.NameKey project, final PatchSet ps) {
+ final StringBuilder r = new StringBuilder();
+ p(r, project);
+ a(r, "commit");
+ h(r, ps);
+ return baseUrl + r;
+ }
+
+ public String toProject(final Project.NameKey project) {
+ final StringBuilder r = new StringBuilder();
+ p(r, project);
+ a(r, "summary");
+ return baseUrl + r;
+ }
+
+ public String toBranch(final Branch.NameKey branch) {
+ final StringBuilder r = new StringBuilder();
+ p(r, branch.getParentKey());
+ h(r, branch);
+ a(r, "shortlog");
+ return baseUrl + r;
+ }
+
+ private static void p(final StringBuilder r, final Project.NameKey project) {
+ String n = project.get();
+ if (!n.endsWith(".git")) {
+ n += ".git";
+ }
+ var(r, "p", n);
+ }
+
+ private static void h(final StringBuilder r, final PatchSet ps) {
+ var(r, "h", ps.getRevision().get());
+ }
+
+ private static void h(final StringBuilder r, final Branch.NameKey branch) {
+ var(r, "h", branch.get());
+ }
+
+ private static void a(final StringBuilder r, final String where) {
+ var(r, "a", where);
+ }
+
+ private static void var(final StringBuilder r, final String n, final String v) {
+ if (r.length() > 0) {
+ r.append(";");
+ }
+ r.append(n);
+ r.append("=");
+ r.append(URL.encodeComponent(v));
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
new file mode 100644
index 0000000000..0cb2a8b0d6
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface GroupAdminService extends RemoteJsonService {
+ @SignInRequired
+ void ownedGroups(AsyncCallback<List<AccountGroup>> callback);
+
+ @SignInRequired
+ void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
+
+ @SignInRequired
+ void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback);
+
+ @SignInRequired
+ void changeGroupDescription(AccountGroup.Id groupId, String description,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void changeGroupOwner(AccountGroup.Id groupId, String newOwnerName,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void renameGroup(AccountGroup.Id groupId, String newName,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void changeGroupType(AccountGroup.Id groupId, AccountGroup.Type newType,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void changeExternalGroup(AccountGroup.Id groupId,
+ AccountGroup.ExternalNameKey bindTo, AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void searchExternalGroups(String searchFilter,
+ AsyncCallback<List<AccountGroup.ExternalNameKey>> callback);
+
+ @SignInRequired
+ void addGroupMember(AccountGroup.Id groupId, String nameOrEmail,
+ AsyncCallback<GroupDetail> callback);
+
+ @SignInRequired
+ void deleteGroupMembers(AccountGroup.Id groupId,
+ Set<AccountGroupMember.Key> keys, AsyncCallback<VoidResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
new file mode 100644
index 0000000000..c31db51aae
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupDetail.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+
+import java.util.List;
+
+public class GroupDetail {
+ public AccountInfoCache accounts;
+ public AccountGroup group;
+ public List<AccountGroupMember> members;
+ public AccountGroup ownerGroup;
+
+ public GroupDetail() {
+ }
+
+ public void setAccounts(AccountInfoCache c) {
+ accounts = c;
+ }
+
+ public void setGroup(AccountGroup g) {
+ group = g;
+ }
+
+ public void setMembers(List<AccountGroupMember> m) {
+ members = m;
+ }
+
+ public void setOwnerGroup(AccountGroup g) {
+ ownerGroup = g;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
new file mode 100644
index 0000000000..31c9d52002
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Data sent as part of the host page, to bootstrap the UI. */
+public class HostPageData {
+ public Account userAccount;
+ public GerritConfig config;
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
new file mode 100644
index 0000000000..6cb51c7f11
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchDetailService.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Patch.Key;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface PatchDetailService extends RemoteJsonService {
+ void patchScript(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
+ PatchScriptSettings settings, AsyncCallback<PatchScript> callback);
+
+ void patchComments(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
+ AsyncCallback<CommentDetail> callback);
+
+ @SignInRequired
+ void saveDraft(PatchLineComment comment,
+ AsyncCallback<PatchLineComment> callback);
+
+ @SignInRequired
+ void deleteDraft(PatchLineComment.Key key, AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void publishComments(PatchSet.Id psid, String message,
+ Set<ApprovalCategoryValue.Id> approvals,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void addReviewers(Change.Id id, List<String> reviewers,
+ AsyncCallback<AddReviewerResult> callback);
+
+ void userApprovals(Set<Change.Id> cids, Account.Id aid,
+ AsyncCallback<ApprovalSummarySet> callback);
+
+ void strongestApprovals(Set<Change.Id> cids,
+ AsyncCallback<ApprovalSummarySet> callback);
+
+ /**
+ * Update the reviewed status for the patch.
+ */
+ @SignInRequired
+ void setReviewedByCurrentUser(Key patchKey, boolean reviewed, AsyncCallback<VoidResult> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
new file mode 100644
index 0000000000..89444b3f8d
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScript.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+
+
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.reviewdb.Change;
+
+import org.eclipse.jgit.diff.Edit;
+
+import java.util.List;
+
+public class PatchScript {
+ public static enum DisplayMethod {
+ NONE, DIFF, IMG
+ }
+
+ protected Change.Key changeId;
+ protected List<String> header;
+ protected PatchScriptSettings settings;
+ protected SparseFileContent a;
+ protected SparseFileContent b;
+ protected List<Edit> edits;
+ protected DisplayMethod displayMethodA;
+ protected DisplayMethod displayMethodB;
+
+ public PatchScript(final Change.Key ck, final List<String> h,
+ final PatchScriptSettings s, final SparseFileContent ca,
+ final SparseFileContent cb, final List<Edit> e, final DisplayMethod ma,
+ final DisplayMethod mb) {
+ changeId = ck;
+ header = h;
+ settings = s;
+ a = ca;
+ b = cb;
+ edits = e;
+ displayMethodA = ma;
+ displayMethodB = mb;
+ }
+
+ protected PatchScript() {
+ }
+
+ public Change.Key getChangeId() {
+ return changeId;
+ }
+
+ public DisplayMethod getDisplayMethodA() {
+ return displayMethodA;
+ }
+
+ public DisplayMethod getDisplayMethodB() {
+ return displayMethodB;
+ }
+
+ public List<String> getPatchHeader() {
+ return header;
+ }
+
+ public int getContext() {
+ return settings.getContext();
+ }
+
+ public boolean isIgnoreWhitespace() {
+ return settings.getWhitespace() != Whitespace.IGNORE_NONE;
+ }
+
+ public SparseFileContent getA() {
+ return a;
+ }
+
+ public SparseFileContent getB() {
+ return b;
+ }
+
+ public List<Edit> getEdits() {
+ return edits;
+ }
+
+ public Iterable<EditList.Hunk> getHunks() {
+ return new EditList(edits, getContext(), a.size(), b.size()).getHunks();
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScriptSettings.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScriptSettings.java
new file mode 100644
index 0000000000..019ecdf490
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchScriptSettings.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.CodedEnum;
+
+public class PatchScriptSettings {
+ public static enum Whitespace implements CodedEnum {
+ IGNORE_NONE('N'), //
+ IGNORE_SPACE_AT_EOL('E'), //
+ IGNORE_SPACE_CHANGE('S'), //
+ IGNORE_ALL_SPACE('A');
+
+ private final char code;
+
+ private Whitespace(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+ }
+
+ protected int context;
+ protected Whitespace whitespace;
+
+ public PatchScriptSettings() {
+ context = AccountGeneralPreferences.DEFAULT_CONTEXT;
+ whitespace = Whitespace.IGNORE_NONE;
+ }
+
+ public PatchScriptSettings(final PatchScriptSettings s) {
+ context = s.context;
+ whitespace = s.whitespace;
+ }
+
+ public int getContext() {
+ return context;
+ }
+
+ public void setContext(final int ctx) {
+ assert 0 <= ctx || ctx == AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
+ context = ctx;
+ }
+
+ public Whitespace getWhitespace() {
+ return whitespace;
+ }
+
+ public void setWhitespace(final Whitespace ws) {
+ assert ws != null;
+ whitespace = ws;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java
new file mode 100644
index 0000000000..64e666efa5
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+
+import java.util.List;
+
+public class PatchSetDetail {
+ protected PatchSet patchSet;
+ protected PatchSetInfo info;
+ protected List<Patch> patches;
+
+ public PatchSetDetail() {
+ }
+
+ public PatchSet getPatchSet() {
+ return patchSet;
+ }
+
+ public void setPatchSet(final PatchSet ps) {
+ patchSet = ps;
+ }
+
+ public PatchSetInfo getInfo() {
+ return info;
+ }
+
+ public void setInfo(final PatchSetInfo i) {
+ info = i;
+ }
+
+ public List<Patch> getPatches() {
+ return patches;
+ }
+
+ public void setPatches(final List<Patch> p) {
+ patches = p;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
new file mode 100644
index 0000000000..70cc10dda6
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class PatchSetPublishDetail {
+ protected AccountInfoCache accounts;
+ protected PatchSetInfo patchSetInfo;
+ protected Change change;
+ protected List<PatchLineComment> drafts;
+ protected Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
+ protected Map<ApprovalCategory.Id, PatchSetApproval> given;
+
+ public Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> getAllowed() {
+ return allowed;
+ }
+
+ public void setAllowed(
+ Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed) {
+ this.allowed = allowed;
+ }
+
+ public Map<ApprovalCategory.Id, PatchSetApproval> getGiven() {
+ return given;
+ }
+
+ public void setGiven(Map<ApprovalCategory.Id, PatchSetApproval> given) {
+ this.given = given;
+ }
+
+ public void setAccounts(AccountInfoCache accounts) {
+ this.accounts = accounts;
+ }
+
+ public void setPatchSetInfo(PatchSetInfo patchSetInfo) {
+ this.patchSetInfo = patchSetInfo;
+ }
+
+ public void setChange(Change change) {
+ this.change = change;
+ }
+
+ public void setDrafts(List<PatchLineComment> drafts) {
+ this.drafts = drafts;
+ }
+
+ public AccountInfoCache getAccounts() {
+ return accounts;
+ }
+
+ public Change getChange() {
+ return change;
+ }
+
+ public PatchSetInfo getPatchSetInfo() {
+ return patchSetInfo;
+ }
+
+ public List<PatchLineComment> getDrafts() {
+ return drafts;
+ }
+
+ public boolean isAllowed(final ApprovalCategory.Id id) {
+ final Set<ApprovalCategoryValue.Id> s = getAllowed(id);
+ return s != null && !s.isEmpty();
+ }
+
+ public Set<ApprovalCategoryValue.Id> getAllowed(final ApprovalCategory.Id id) {
+ return allowed.get(id);
+ }
+
+ public PatchSetApproval getChangeApproval(final ApprovalCategory.Id id) {
+ return given.get(id);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
new file mode 100644
index 0000000000..5f27e40598
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.List;
+import java.util.Set;
+
+public interface ProjectAdminService extends RemoteJsonService {
+ @SignInRequired
+ void ownedProjects(AsyncCallback<List<Project>> callback);
+
+ @SignInRequired
+ void projectDetail(Project.NameKey projectName,
+ AsyncCallback<ProjectDetail> callback);
+
+ @SignInRequired
+ void changeProjectSettings(Project update,
+ AsyncCallback<ProjectDetail> callback);
+
+ @SignInRequired
+ void deleteRight(Project.NameKey projectName, Set<ProjectRight.Key> ids,
+ AsyncCallback<VoidResult> callback);
+
+ @SignInRequired
+ void addRight(Project.NameKey projectName, ApprovalCategory.Id categoryId,
+ String groupName, short min, short max,
+ AsyncCallback<ProjectDetail> callback);
+
+ @SignInRequired
+ void listBranches(Project.NameKey projectName,
+ AsyncCallback<List<Branch>> callback);
+
+ @SignInRequired
+ void addBranch(Project.NameKey projectName, String branchName,
+ String startingRevision, AsyncCallback<List<Branch>> callback);
+
+ @SignInRequired
+ void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
+ AsyncCallback<Set<Branch.NameKey>> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
new file mode 100644
index 0000000000..9c4619a8b4
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+
+import java.util.List;
+import java.util.Map;
+
+public class ProjectDetail {
+ public Project project;
+ public Map<AccountGroup.Id, AccountGroup> groups;
+ public List<ProjectRight> rights;
+
+ public ProjectDetail() {
+ }
+
+ public void setProject(final Project p) {
+ project = p;
+ }
+
+ public void setGroups(final Map<AccountGroup.Id, AccountGroup> g) {
+ groups = g;
+ }
+
+ public void setRights(final List<ProjectRight> r) {
+ rights = r;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectInfo.java
new file mode 100644
index 0000000000..8f2293630e
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectInfo.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Project;
+
+public class ProjectInfo {
+ protected Project.NameKey key;
+
+ protected ProjectInfo() {
+ }
+
+ public ProjectInfo(final Project.NameKey key) {
+ this.key = key;
+ }
+
+ public Project.NameKey getKey() {
+ return key;
+ }
+
+ public String getName() {
+ return key.get();
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SingleListChangeInfo.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SingleListChangeInfo.java
new file mode 100644
index 0000000000..e55375bdbe
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SingleListChangeInfo.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import java.util.List;
+
+/** Summary information needed for screens showing a single list of changes}. */
+public class SingleListChangeInfo {
+ protected AccountInfoCache accounts;
+ protected List<ChangeInfo> changes;
+ protected boolean atEnd;
+
+ public SingleListChangeInfo() {
+ }
+
+ public AccountInfoCache getAccounts() {
+ return accounts;
+ }
+
+ public void setAccounts(final AccountInfoCache ac) {
+ accounts = ac;
+ }
+
+ public List<ChangeInfo> getChanges() {
+ return changes;
+ }
+
+ public boolean isAtEnd() {
+ return atEnd;
+ }
+
+ public void setChanges(List<ChangeInfo> c) {
+ setChanges(c, true);
+ }
+
+ public void setChanges(List<ChangeInfo> c, boolean end) {
+ changes = c;
+ atEnd = end;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SparseFileContent.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SparseFileContent.java
new file mode 100644
index 0000000000..99913c61f8
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SparseFileContent.java
@@ -0,0 +1,153 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SparseFileContent {
+ protected List<Range> ranges;
+ protected int size;
+ protected boolean missingNewlineAtEnd;
+
+ private transient int currentRangeIdx;
+
+ public SparseFileContent() {
+ ranges = new ArrayList<Range>();
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public void setSize(final int s) {
+ size = s;
+ }
+
+ public boolean isMissingNewlineAtEnd() {
+ return missingNewlineAtEnd;
+ }
+
+ public void setMissingNewlineAtEnd(final boolean missing) {
+ missingNewlineAtEnd = missing;
+ }
+
+ public String get(final int idx) {
+ final String line = getLine(idx);
+ if (line == null) {
+ throw new ArrayIndexOutOfBoundsException(idx);
+ }
+ return line;
+ }
+
+ public boolean contains(final int idx) {
+ return getLine(idx) != null;
+ }
+
+ private String getLine(final int idx) {
+ // Most requests are sequential in nature, fetching the next
+ // line from the current range, or the next range.
+ //
+ int high = ranges.size();
+ if (currentRangeIdx < high) {
+ Range cur = ranges.get(currentRangeIdx);
+ if (cur.contains(idx)) {
+ return cur.get(idx);
+ }
+
+ if (++currentRangeIdx < high) {
+ final Range next = ranges.get(currentRangeIdx);
+ if (next.contains(idx)) {
+ return next.get(idx);
+ }
+ }
+ }
+
+ // Binary search for the range, since we know its a sorted list.
+ //
+ int low = 0;
+ do {
+ final int mid = (low + high) / 2;
+ final Range cur = ranges.get(mid);
+ if (cur.contains(idx)) {
+ currentRangeIdx = mid;
+ return cur.get(idx);
+ }
+ if (idx < cur.base)
+ high = mid;
+ else
+ low = mid + 1;
+ } while (low < high);
+ return null;
+ }
+
+ public void addLine(final int i, final String content) {
+ final Range r;
+ if (!ranges.isEmpty() && i == last().end()) {
+ r = last();
+ } else {
+ r = new Range(i);
+ ranges.add(r);
+ }
+ r.lines.add(content);
+ }
+
+ private Range last() {
+ return ranges.get(ranges.size() - 1);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder b = new StringBuilder();
+ b.append("SparseFileContent[\n");
+ for (Range r : ranges) {
+ b.append(" ");
+ b.append(r.toString());
+ b.append('\n');
+ }
+ b.append("]");
+ return b.toString();
+ }
+
+ static class Range {
+ protected int base;
+ protected List<String> lines;
+
+ private Range(final int b) {
+ base = b;
+ lines = new ArrayList<String>();
+ }
+
+ protected Range() {
+ }
+
+ private String get(final int i) {
+ return lines.get(i - base);
+ }
+
+ private int end() {
+ return base + lines.size();
+ }
+
+ private boolean contains(final int i) {
+ return base <= i && i < end();
+ }
+
+ @Override
+ public String toString() {
+ return "Range[" + base + "," + end() + ")";
+ }
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SshHostKey.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SshHostKey.java
new file mode 100644
index 0000000000..1d4e3c9568
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SshHostKey.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+/** Description of the SSH daemon host key used by Gerrit. */
+public class SshHostKey {
+ protected String hostIdent;
+ protected String hostKey;
+ protected String fingerprint;
+
+ protected SshHostKey() {
+ }
+
+ public SshHostKey(final String hi, final String hk, final String fp) {
+ hostIdent = hi;
+ hostKey = hk;
+ fingerprint = fp;
+ }
+
+ /** @return host name string, to appear in a known_hosts file. */
+ public String getHostIdent() {
+ return hostIdent;
+ }
+
+ /** @return base 64 encoded host key string, starting with key type. */
+ public String getHostKey() {
+ return hostKey;
+ }
+
+ /** @return the key fingerprint, as displayed by a connecting client. */
+ public String getFingerprint() {
+ return fingerprint;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
new file mode 100644
index 0000000000..b31ace2f9b
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+import java.util.List;
+
+public interface SuggestService extends RemoteJsonService {
+ void suggestProjectNameKey(String query, int limit,
+ AsyncCallback<List<Project.NameKey>> callback);
+
+ void suggestAccount(String query, int limit,
+ AsyncCallback<List<AccountInfo>> callback);
+
+ void suggestAccountGroup(String query, int limit,
+ AsyncCallback<List<AccountGroup>> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java
new file mode 100644
index 0000000000..d2322a1803
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SystemInfoService.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+import java.util.List;
+
+public interface SystemInfoService extends RemoteJsonService {
+ @AllowCrossSiteRequest
+ void daemonHostKeys(AsyncCallback<List<SshHostKey>> callback);
+
+ @SignInRequired
+ void contributorAgreements(AsyncCallback<List<ContributorAgreement>> callback);
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ToggleStarRequest.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ToggleStarRequest.java
new file mode 100644
index 0000000000..32178fbbb0
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ToggleStarRequest.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.data;
+
+import com.google.gerrit.reviewdb.Change;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Request parameters to update the changes the user has toggled. */
+public class ToggleStarRequest {
+ protected Set<Change.Id> add;
+ protected Set<Change.Id> remove;
+
+ /**
+ * Request an update to the change's star status.
+ *
+ * @param id unique id of the change, must not be null.
+ * @param on true if the change should now be starred; false if it should now
+ * be not starred.
+ */
+ public void toggle(final Change.Id id, final boolean on) {
+ if (on) {
+ if (add == null) {
+ add = new HashSet<Change.Id>();
+ }
+ add.add(id);
+ if (remove != null) {
+ remove.remove(id);
+ }
+ } else {
+ if (remove == null) {
+ remove = new HashSet<Change.Id>();
+ }
+ remove.add(id);
+ if (add != null) {
+ add.remove(id);
+ }
+ }
+ }
+
+ /** Get the set of changes which should have stars added; may be null. */
+ public Set<Change.Id> getAddSet() {
+ return add;
+ }
+
+ /** Get the set of changes which should have stars removed; may be null. */
+ public Set<Change.Id> getRemoveSet() {
+ return remove;
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java
new file mode 100644
index 0000000000..956d010a63
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/ContactInformationStoreException.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the server cannot store contact information. */
+public class ContactInformationStoreException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Cannot store contact information";
+
+ public ContactInformationStoreException() {
+ super(MESSAGE);
+ }
+
+ public ContactInformationStoreException(final Throwable why) {
+ super(MESSAGE, why);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/CorruptEntityException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/CorruptEntityException.java
new file mode 100644
index 0000000000..f2b3e98908
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/CorruptEntityException.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+import com.google.gwtorm.client.Key;
+
+/** Error indicating the entity's database records are invalid. */
+public class CorruptEntityException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE_PREFIX = "Corrupt Database Entity: ";
+
+ public CorruptEntityException(final Key<?> key) {
+ super(MESSAGE_PREFIX + key);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidNameException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidNameException.java
new file mode 100644
index 0000000000..5eb6e3f0b7
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidNameException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the entity name is invalid as supplied. */
+public class InvalidNameException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid Name";
+
+ public InvalidNameException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidRevisionException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidRevisionException.java
new file mode 100644
index 0000000000..b4b54c16bb
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidRevisionException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the revision is invalid as supplied. */
+public class InvalidRevisionException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid Revision";
+
+ public InvalidRevisionException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshKeyException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshKeyException.java
new file mode 100644
index 0000000000..3398417a98
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshKeyException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the SSH key string is invalid as supplied. */
+public class InvalidSshKeyException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid SSH Key";
+
+ public InvalidSshKeyException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshUserNameException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshUserNameException.java
new file mode 100644
index 0000000000..4608d51dd6
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/InvalidSshUserNameException.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Error indicating the SSH user name does not match {@link Account#SSH_USER_NAME_PATTERN} pattern. */
+public class InvalidSshUserNameException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Invalid SSH user name.";
+
+ public InvalidSshUserNameException() {
+ super(MESSAGE);
+ }
+
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
new file mode 100644
index 0000000000..a2d487bdfc
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NameAlreadyUsedException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating entity name is already taken by another entity. */
+public class NameAlreadyUsedException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Name Already Used";
+
+ public NameAlreadyUsedException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchAccountException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchAccountException.java
new file mode 100644
index 0000000000..90bf624928
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchAccountException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the account requested doesn't exist. */
+public class NoSuchAccountException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Found: ";
+
+ public NoSuchAccountException(String who) {
+ super(MESSAGE + who);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchEntityException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchEntityException.java
new file mode 100644
index 0000000000..c47cf07cc0
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchEntityException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error indicating the entity requested doesn't exist. */
+public class NoSuchEntityException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Found";
+
+ public NoSuchEntityException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NotSignedInException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NotSignedInException.java
new file mode 100644
index 0000000000..65caf020dc
--- /dev/null
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NotSignedInException.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.common.errors;
+
+/** Error stating the user must be signed-in in order to perform this action. */
+public class NotSignedInException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public static final String MESSAGE = "Not Signed In";
+
+ public NotSignedInException() {
+ super(MESSAGE);
+ }
+}
diff --git a/gerrit-gwtdebug/.gitignore b/gerrit-gwtdebug/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-gwtdebug/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs b/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-gwtdebug/.settings/org.eclipse.core.runtime.prefs b/gerrit-gwtdebug/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-gwtdebug/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-gwtdebug/.settings/org.eclipse.jdt.core.prefs b/gerrit-gwtdebug/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-gwtdebug/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-gwtdebug/.settings/org.eclipse.jdt.ui.prefs b/gerrit-gwtdebug/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-gwtdebug/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-gwtdebug/pom.xml b/gerrit-gwtdebug/pom.xml
new file mode 100644
index 0000000000..7173e98e90
--- /dev/null
+++ b/gerrit-gwtdebug/pom.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-gwtdbug</artifactId>
+ <name>Gerrit Code Review - GWT UI Debugging Support</name>
+
+ <description>
+ Debugging support for the GWT UI
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-dev</artifactId>
+ <classifier>${platform}</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-gwtui</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-war</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcprov-jdk15</artifactId>
+ <version>140</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcpg-jdk15</artifactId>
+ <version>140</version>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java
new file mode 100644
index 0000000000..d5f913a6ab
--- /dev/null
+++ b/gerrit-gwtdebug/src/main/java/com/google/gerrit/gwtdebug/GerritDebugLauncher.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright 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 com.google.gerrit.gwtdebug;
+
+import com.google.gwt.core.ext.ServletContainer;
+import com.google.gwt.core.ext.ServletContainerLauncher;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.shell.jetty.JettyNullLogger;
+
+import org.mortbay.component.AbstractLifeCycle;
+import org.mortbay.jetty.AbstractConnector;
+import org.mortbay.jetty.Request;
+import org.mortbay.jetty.RequestLog;
+import org.mortbay.jetty.Response;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.HttpFields.Field;
+import org.mortbay.jetty.handler.RequestLogHandler;
+import org.mortbay.jetty.nio.SelectChannelConnector;
+import org.mortbay.jetty.webapp.WebAppClassLoader;
+import org.mortbay.jetty.webapp.WebAppContext;
+import org.mortbay.log.Log;
+import org.mortbay.log.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Iterator;
+
+public class GerritDebugLauncher extends ServletContainerLauncher {
+ /**
+ * Log jetty requests/responses to TreeLogger.
+ */
+ public static class JettyRequestLogger extends AbstractLifeCycle implements
+ RequestLog {
+
+ private final TreeLogger logger;
+
+ public JettyRequestLogger(TreeLogger logger) {
+ this.logger = logger;
+ }
+
+ /**
+ * Log an HTTP request/response to TreeLogger.
+ */
+ @SuppressWarnings("unchecked")
+ public void log(Request request, Response response) {
+ int status = response.getStatus();
+ if (status < 0) {
+ // Copied from NCSARequestLog
+ status = 404;
+ }
+ TreeLogger.Type logStatus, logHeaders;
+ if (status >= 500) {
+ logStatus = TreeLogger.ERROR;
+ logHeaders = TreeLogger.INFO;
+ } else if (status >= 400) {
+ logStatus = TreeLogger.WARN;
+ logHeaders = TreeLogger.INFO;
+ } else {
+ logStatus = TreeLogger.INFO;
+ logHeaders = TreeLogger.DEBUG;
+ }
+ String userString = request.getRemoteUser();
+ if (userString == null) {
+ userString = "";
+ } else {
+ userString += "@";
+ }
+ String bytesString = "";
+ if (response.getContentCount() > 0) {
+ bytesString = " " + response.getContentCount() + " bytes";
+ }
+ if (logger.isLoggable(logStatus)) {
+ TreeLogger branch =
+ logger.branch(logStatus, String.valueOf(status) + " - "
+ + request.getMethod() + ' ' + request.getUri() + " ("
+ + userString + request.getRemoteHost() + ')' + bytesString);
+ if (branch.isLoggable(logHeaders)) {
+ // Request headers
+ TreeLogger headers = branch.branch(logHeaders, "Request headers");
+ Iterator<Field> headerFields =
+ request.getConnection().getRequestFields().getFields();
+ while (headerFields.hasNext()) {
+ Field headerField = headerFields.next();
+ headers.log(logHeaders, headerField.getName() + ": "
+ + headerField.getValue());
+ }
+ // Response headers
+ headers = branch.branch(logHeaders, "Response headers");
+ headerFields = response.getHttpFields().getFields();
+ while (headerFields.hasNext()) {
+ Field headerField = headerFields.next();
+ headers.log(logHeaders, headerField.getName() + ": "
+ + headerField.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * An adapter for the Jetty logging system to GWT's TreeLogger. This
+ * implementation class is only public to allow {@link Log} to instantiate it.
+ *
+ * The weird static data / default construction setup is a game we play with
+ * {@link Log}'s static initializer to prevent the initial log message from
+ * going to stderr.
+ */
+ public static class JettyTreeLogger implements Logger {
+ private final TreeLogger logger;
+
+ public JettyTreeLogger(TreeLogger logger) {
+ if (logger == null) {
+ throw new NullPointerException();
+ }
+ this.logger = logger;
+ }
+
+ public void debug(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.SPAM, format(msg, arg0, arg1));
+ }
+
+ public void debug(String msg, Throwable th) {
+ logger.log(TreeLogger.SPAM, msg, th);
+ }
+
+ public Logger getLogger(String name) {
+ return this;
+ }
+
+ public void info(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.INFO, format(msg, arg0, arg1));
+ }
+
+ public boolean isDebugEnabled() {
+ return logger.isLoggable(TreeLogger.SPAM);
+ }
+
+ public void setDebugEnabled(boolean enabled) {
+ // ignored
+ }
+
+ public void warn(String msg, Object arg0, Object arg1) {
+ logger.log(TreeLogger.WARN, format(msg, arg0, arg1));
+ }
+
+ public void warn(String msg, Throwable th) {
+ logger.log(TreeLogger.WARN, msg, th);
+ }
+
+ /**
+ * Copied from org.mortbay.log.StdErrLog.
+ */
+ private String format(String msg, Object arg0, Object arg1) {
+ int i0 = msg.indexOf("{}");
+ int i1 = i0 < 0 ? -1 : msg.indexOf("{}", i0 + 2);
+
+ if (arg1 != null && i1 >= 0) {
+ msg = msg.substring(0, i1) + arg1 + msg.substring(i1 + 2);
+ }
+ if (arg0 != null && i0 >= 0) {
+ msg = msg.substring(0, i0) + arg0 + msg.substring(i0 + 2);
+ }
+ return msg;
+ }
+ }
+
+ /**
+ * The resulting {@link ServletContainer} this is launched.
+ */
+ protected static class JettyServletContainer extends ServletContainer {
+ private final int actualPort;
+ private final File appRootDir;
+ private final TreeLogger logger;
+ private final Server server;
+ private final WebAppContext wac;
+
+ public JettyServletContainer(TreeLogger logger, Server server,
+ WebAppContext wac, int actualPort, File appRootDir) {
+ this.logger = logger;
+ this.server = server;
+ this.wac = wac;
+ this.actualPort = actualPort;
+ this.appRootDir = appRootDir;
+ }
+
+ @Override
+ public int getPort() {
+ return actualPort;
+ }
+
+ @Override
+ public void refresh() throws UnableToCompleteException {
+ String msg =
+ "Reloading web app to reflect changes in "
+ + appRootDir.getAbsolutePath();
+ TreeLogger branch = logger.branch(TreeLogger.INFO, msg);
+ // Temporarily log Jetty on the branch.
+ Log.setLog(new JettyTreeLogger(branch));
+ try {
+ wac.stop();
+ wac.start();
+ branch.log(TreeLogger.INFO, "Reload completed successfully");
+ } catch (Exception e) {
+ branch.log(TreeLogger.ERROR, "Unable to restart embedded Jetty server",
+ e);
+ throw new UnableToCompleteException();
+ } finally {
+ // Reset the top-level logger.
+ Log.setLog(new JettyTreeLogger(logger));
+ }
+ }
+
+ @Override
+ public void stop() throws UnableToCompleteException {
+ TreeLogger branch =
+ logger.branch(TreeLogger.INFO, "Stopping Jetty server");
+ // Temporarily log Jetty on the branch.
+ Log.setLog(new JettyTreeLogger(branch));
+ try {
+ server.stop();
+ server.setStopAtShutdown(false);
+ branch.log(TreeLogger.INFO, "Stopped successfully");
+ } catch (Exception e) {
+ branch.log(TreeLogger.ERROR, "Unable to stop embedded Jetty server", e);
+ throw new UnableToCompleteException();
+ } finally {
+ // Reset the top-level logger.
+ Log.setLog(new JettyTreeLogger(logger));
+ }
+ }
+ }
+
+ /**
+ * A {@link WebAppContext} tailored to GWT hosted mode. Features hot-reload
+ * with a new {@link WebAppClassLoader} to pick up disk changes. The default
+ * Jetty {@code WebAppContext} will create new instances of servlets, but it
+ * will not create a brand new {@link ClassLoader}. By creating a new {@code
+ * ClassLoader} each time, we re-read updated classes from disk.
+ *
+ * Also provides special class filtering to isolate the web app from the GWT
+ * hosting environment.
+ */
+ protected final class MyWebAppContext extends WebAppContext {
+ /**
+ * Parent ClassLoader for the Jetty web app, which can only load JVM
+ * classes. We would just use <code>null</code> for the parent ClassLoader
+ * except this makes Jetty unhappy.
+ */
+ private final ClassLoader bootStrapOnlyClassLoader =
+ new ClassLoader(null) {};
+
+ private final ClassLoader systemClassLoader =
+ Thread.currentThread().getContextClassLoader();
+
+ private final TreeLogger logger;
+
+ @SuppressWarnings("unchecked")
+ private MyWebAppContext(TreeLogger logger, String webApp, String contextPath) {
+ super(webApp, contextPath);
+ this.logger = logger;
+
+ // Prevent file locking on Windows; pick up file changes.
+ getInitParams().put(
+ "org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false");
+
+ // Since the parent class loader is bootstrap-only, prefer it first.
+ setParentLoaderPriority(true);
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ setClassLoader(new MyLoader());
+ super.doStart();
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+ setClassLoader(null);
+ }
+
+ private class MyLoader extends WebAppClassLoader {
+ MyLoader() throws IOException {
+ super(bootStrapOnlyClassLoader, MyWebAppContext.this);
+
+ final URLClassLoader scl = (URLClassLoader) systemClassLoader;
+ final URL[] urls = scl.getURLs();
+ for (URL u : urls) {
+ if ("file".equals(u.getProtocol())) {
+ addClassPath(u.getPath());
+ }
+ }
+ }
+
+ @Override
+ public boolean isSystemPath(String name) {
+ name = name.replace('/', '.');
+ return super.isSystemPath(name) //
+ || name.startsWith("org.bouncycastle.");
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ // For system path, always prefer the outside world.
+ if (isSystemPath(name)) {
+ try {
+ return systemClassLoader.loadClass(name);
+ } catch (ClassNotFoundException e) {
+ }
+ }
+ return super.findClass(name);
+ }
+ }
+ }
+
+ static {
+ // Suppress spammy Jetty log initialization.
+ System
+ .setProperty("org.mortbay.log.class", JettyNullLogger.class.getName());
+ Log.getLog();
+
+ /*
+ * Make JDT the default Ant compiler so that JSP compilation just works
+ * out-of-the-box. If we don't set this, it's very, very difficult to make
+ * JSP compilation work.
+ */
+ String antJavaC =
+ System.getProperty("build.compiler",
+ "org.eclipse.jdt.core.JDTCompilerAdapter");
+ System.setProperty("build.compiler", antJavaC);
+ }
+
+ @Override
+ public ServletContainer start(TreeLogger logger, int port, File warDir)
+ throws Exception {
+ TreeLogger branch =
+ logger.branch(TreeLogger.INFO, "Starting Jetty on port " + port, null);
+
+ checkStartParams(branch, port, warDir);
+
+ // Setup our branch logger during startup.
+ Log.setLog(new JettyTreeLogger(branch));
+
+ // Turn off XML validation.
+ System.setProperty("org.mortbay.xml.XmlParser.Validating", "false");
+
+ AbstractConnector connector = getConnector();
+ connector.setPort(port);
+
+ // Don't share ports with an existing process.
+ connector.setReuseAddress(false);
+
+ // Linux keeps the port blocked after shutdown if we don't disable this.
+ connector.setSoLingerTime(0);
+
+ Server server = new Server();
+ server.addConnector(connector);
+
+ // warDir is "$top/gerrit-gwtui/target/gwt-hosted-mode"
+ //
+ File top = warDir.getParentFile().getParentFile().getParentFile();
+ File app = new File(top, "gerrit-war/src/main/webapp");
+ File webxml = new File(app, "WEB-INF/web.xml");
+
+ // Create a new web app in the war directory.
+ //
+ WebAppContext wac =
+ new MyWebAppContext(logger, warDir.getAbsolutePath(), "/");
+ wac.setDescriptor(webxml.getAbsolutePath());
+
+ RequestLogHandler logHandler = new RequestLogHandler();
+ logHandler.setRequestLog(new JettyRequestLogger(logger));
+ logHandler.setHandler(wac);
+ server.setHandler(logHandler);
+ server.start();
+ server.setStopAtShutdown(true);
+
+ // Now that we're started, log to the top level logger.
+ Log.setLog(new JettyTreeLogger(logger));
+
+ return new JettyServletContainer(logger, server, wac, connector
+ .getLocalPort(), warDir);
+ }
+
+ protected AbstractConnector getConnector() {
+ return new SelectChannelConnector();
+ }
+
+ private void checkStartParams(TreeLogger logger, int port, File appRootDir) {
+ if (logger == null) {
+ throw new NullPointerException("logger cannot be null");
+ }
+
+ if (port < 0 || port > 65535) {
+ throw new IllegalArgumentException(
+ "port must be either 0 (for auto) or less than 65536");
+ }
+
+ if (appRootDir == null) {
+ throw new NullPointerException("app root direcotry cannot be null");
+ }
+ }
+}
diff --git a/gerrit-gwtui/.gitignore b/gerrit-gwtui/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-gwtui/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs b/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-gwtui/.settings/org.eclipse.core.runtime.prefs b/gerrit-gwtui/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-gwtui/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-gwtui/.settings/org.eclipse.jdt.core.prefs b/gerrit-gwtui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-gwtui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-gwtui/.settings/org.eclipse.jdt.ui.prefs b/gerrit-gwtui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-gwtui/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-gwtui/pom.xml b/gerrit-gwtui/pom.xml
new file mode 100644
index 0000000000..7f14be565d
--- /dev/null
+++ b/gerrit-gwtui/pom.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-gwtui</artifactId>
+ <name>Gerrit Code Review - GWT UI</name>
+ <packaging>war</packaging>
+
+ <description>
+ Web interface built on top of Google Web Toolkit
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <version>${gwtVersion}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtorm</groupId>
+ <artifactId>gwtorm</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>gwtorm</groupId>
+ <artifactId>gwtorm</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-reviewdb</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-reviewdb</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-common</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-common</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-jgit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-jgit</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-gwtexpui</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-gwtexpui</artifactId>
+ <scope>compile</scope>
+ <classifier>sources</classifier>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <version>1.1</version>
+ <configuration>
+ <module>com.google.gerrit.GerritGwtUI</module>
+ <extraJvmArgs>-Xmx512m</extraJvmArgs>
+ <style>${gwtStyle}</style>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>get-keyapplet</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>gerrit</groupId>
+ <artifactId>gerrit-keyapplet</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/gerrit-keyapplet</outputDirectory>
+ <includes>**/*</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <packagingExcludes>WEB-INF/classes/**,WEB-INF/lib/**</packagingExcludes>
+ <archive>
+ <addMavenDescriptor>false</addMavenDescriptor>
+ </archive>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>compress-html</id>
+ <phase>process-classes</phase>
+ <configuration>
+ <tasks>
+ <property name="dst" location="${project.build.directory}/${project.build.finalName}"/>
+ <property name="app" location="${dst}/gerrit"/>
+
+ <mkdir dir="${app}"/>
+ <apply executable="gzip" addsourcefile="false">
+ <arg value="-9"/>
+ <fileset dir="${app}"
+ includes="**/*.html,**/*.css"/>
+ <redirector>
+ <inputmapper type="glob" from="*" to="${app}/*"/>
+ <outputmapper type="glob" from="*" to="${app}/*.gz"/>
+ </redirector>
+ </apply>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>include-keyapplet</id>
+ <phase>process-classes</phase>
+ <configuration>
+ <tasks>
+ <property name="dst" location="${project.build.directory}/${project.build.finalName}"/>
+ <property name="app" location="${dst}/gerrit"/>
+ <property name="src" location="${basedir}/target/gerrit-keyapplet" />
+
+ <mkdir dir="${app}"/>
+ <zip
+ destfile="${app}/gerrit-keyapplet.cache.jar"
+ compress="true"
+ level="9">
+ <fileset dir="${src}" includes="**/*" />
+ </zip>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
new file mode 100644
index 0000000000..dfdf3c495f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright (C) 2008 The Android Open Source Project
+
+ 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.
+-->
+<module rename-to="gerrit">
+ <inherits name='com.google.gwt.user.User'/>
+ <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
+ <inherits name='com.google.gwtexpui.css.CSS'/>
+ <inherits name='com.google.gwtexpui.clippy.Clippy'/>
+ <inherits name='com.google.gwtexpui.globalkey.GlobalKey'/>
+ <inherits name='com.google.gwtexpui.progress.Progress'/>
+ <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
+ <inherits name='com.google.gwtexpui.safehtml.PrettyFormatter'/>
+ <inherits name='com.google.gerrit.Common'/>
+ <inherits name='com.google.gerrit.UserAgent'/>
+ <inherits name='org.eclipse.jgit.JGit'/>
+ <stylesheet src='gerrit.css' />
+ <extend-property name="locale" values="en"/>
+
+ <entry-point class='com.google.gerrit.client.Gerrit'/>
+</module>
diff --git a/src/main/java/com/google/gerrit/UserAgent.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml
index 26458139e1..26458139e1 100644
--- a/src/main/java/com/google/gerrit/UserAgent.gwt.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/UserAgent.gwt.xml
diff --git a/src/main/java/com/google/gerrit/client/ErrorDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
index be81df94e4..be81df94e4 100644
--- a/src/main/java/com/google/gerrit/client/ErrorDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ErrorDialog.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
new file mode 100644
index 0000000000..281c72b696
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gwt.i18n.client.DateTimeFormat;
+
+import java.util.Date;
+
+/** Misc. formatting functions. */
+public class FormatUtil {
+ private static final long ONE_YEAR = 182L * 24 * 60 * 60 * 1000;
+
+ private static final DateTimeFormat sTime =
+ DateTimeFormat.getShortTimeFormat();
+ private static final DateTimeFormat sDate = DateTimeFormat.getFormat("MMM d");
+ private static final DateTimeFormat mDate =
+ DateTimeFormat.getMediumDateFormat();
+ private static final DateTimeFormat dtfmt =
+ DateTimeFormat.getFormat(mDate.getPattern() + " " + sTime.getPattern());
+
+ /** Format a date using a really short format. */
+ public static String shortFormat(Date dt) {
+ if (dt == null) {
+ return "";
+ }
+
+ final Date now = new Date();
+ dt = new Date(dt.getTime());
+ if (mDate.format(now).equals(mDate.format(dt))) {
+ // Same day as today, report only the time.
+ //
+ return sTime.format(dt);
+
+ } else if (Math.abs(now.getTime() - dt.getTime()) < ONE_YEAR) {
+ // Within the last year, show a shorter date.
+ //
+ return sDate.format(dt);
+
+ } else {
+ // Report only date and year, its far away from now.
+ //
+ return mDate.format(dt);
+ }
+ }
+
+ /** Format a date using the locale's medium length format. */
+ public static String mediumFormat(final Date dt) {
+ if (dt == null) {
+ return "";
+ }
+ return dtfmt.format(new Date(dt.getTime()));
+ }
+
+ /** Format an account as a name and email address. */
+ public static String nameEmail(final Account acct) {
+ return nameEmail(new AccountInfo(acct));
+ }
+
+ /**
+ * Formats an account as an name and an email address.
+ * <p>
+ * Example output:
+ * <ul>
+ * <li><code>A U. Thor &lt;author@example.com&gt;</code>: full populated</li>
+ * <li><code>A U. Thor (12)</code>: missing email address</li>
+ * <li><code>Anonymous Coward &lt;author@example.com&gt;</code>: missing name</li>
+ * <li><code>Anonymous Coward (12)</code>: missing name and email address</li>
+ * </ul>
+ */
+ public static String nameEmail(final AccountInfo acct) {
+ String name = acct.getFullName();
+ if (name == null) {
+ name = Gerrit.C.anonymousCoward();
+ }
+
+ final StringBuilder b = new StringBuilder();
+ b.append(name);
+ if (acct.getPreferredEmail() != null) {
+ b.append(" <");
+ b.append(acct.getPreferredEmail());
+ b.append(">");
+ } else if (acct.getId() != null) {
+ b.append(" (");
+ b.append(acct.getId().get());
+ b.append(")");
+ }
+ return b.toString();
+ }
+
+ /**
+ * Formats an account name.
+ * <p>
+ * If the account has a full name, it returns only the full name. Otherwise it
+ * returns a longer form that includes the email address.
+ */
+ public static String name(final AccountInfo ai) {
+ if (ai.getFullName() != null) {
+ return ai.getFullName();
+ }
+ if (ai.getPreferredEmail() != null) {
+ return ai.getPreferredEmail();
+ }
+ return nameEmail(ai);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
new file mode 100644
index 0000000000..08863d072a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -0,0 +1,404 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
+import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.LinkMenuBar;
+import com.google.gerrit.client.ui.LinkMenuItem;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.Version;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.common.data.HostPageData;
+import com.google.gerrit.common.data.SystemInfoService;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Cookies;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.user.client.ui.Accessibility;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.TabPanel;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
+import com.google.gwtexpui.user.client.UserAgent;
+import com.google.gwtexpui.user.client.ViewSite;
+import com.google.gwtjsonrpc.client.JsonDefTarget;
+import com.google.gwtjsonrpc.client.JsonUtil;
+import com.google.gwtjsonrpc.client.XsrfManager;
+
+import java.util.ArrayList;
+
+public class Gerrit implements EntryPoint {
+ public static final GerritConstants C = GWT.create(GerritConstants.class);
+ public static final GerritMessages M = GWT.create(GerritMessages.class);
+ public static final GerritIcons ICONS = GWT.create(GerritIcons.class);
+ public static final SystemInfoService SYSTEM_SVC;
+
+ private static String myHost;
+ private static String myVersion;
+ private static GerritConfig myConfig;
+ private static Account myAccount;
+
+ private static TabPanel menuLeft;
+ private static LinkMenuBar menuRight;
+ private static RootPanel siteHeader;
+ private static RootPanel siteFooter;
+ private static SearchPanel searchPanel;
+ private static ViewSite<Screen> body;
+
+ static {
+ SYSTEM_SVC = GWT.create(SystemInfoService.class);
+ JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService");
+ }
+
+ public static void display(final String historyToken, final boolean go) {
+ History.newItem(historyToken, go);
+ if (!go && historyHooks != null) {
+ dispatchHistoryHooks(historyToken);
+ }
+ }
+
+ public static void display(final String historyToken, final Screen view) {
+ History.newItem(historyToken, false);
+ display(view);
+ if (historyHooks != null) {
+ dispatchHistoryHooks(historyToken);
+ }
+ }
+
+ public static void display(final Screen view) {
+ if (view.isRequiresSignIn() && !isSignedIn()) {
+ doSignIn();
+ } else {
+ body.setView(view);
+ }
+ }
+
+ public static void setQueryString(String query) {
+ searchPanel.setText(query);
+ }
+
+ public static void setWindowTitle(final Screen screen, final String text) {
+ if (screen == body.getView()) {
+ if (text == null || text.length() == 0) {
+ Window.setTitle(M.windowTitle1(myHost));
+ } else {
+ Window.setTitle(M.windowTitle2(text, myHost));
+ }
+ }
+ }
+
+ /** Get the public configuration data used by this Gerrit instance. */
+ public static GerritConfig getConfig() {
+ return myConfig;
+ }
+
+ /** @return the currently signed in user's account data; null if no account */
+ public static Account getUserAccount() {
+ return myAccount;
+ }
+
+ /** @return true if the user is currently authenticated */
+ public static boolean isSignedIn() {
+ return getUserAccount() != null;
+ }
+
+ /** Sign the user into the application. */
+ public static void doSignIn() {
+ switch (myConfig.getAuthType()) {
+ case HTTP:
+ case HTTP_LDAP:
+ Location.assign(Location.getPath() + "login/" + History.getToken());
+ break;
+
+ case DEVELOPMENT_BECOME_ANY_ACCOUNT:
+ Location.assign(Location.getPath() + "become");
+ break;
+
+ case OPENID:
+ new OpenIdSignInDialog(SignInMode.SIGN_IN, null).center();
+ break;
+
+ case LDAP:
+ new UserPassSignInDialog(null).center();
+ break;
+ }
+ }
+
+ public void onModuleLoad() {
+ UserAgent.assertNotInIFrame();
+ initHostname();
+ Window.setTitle(M.windowTitle1(myHost));
+ initHistoryHooks();
+ populateBottomMenu();
+
+ final RootPanel menuArea = RootPanel.get("gerrit_topmenu");
+ final Grid menuLine = new Grid(1, 3);
+ menuLeft = new TabPanel();
+ menuRight = new LinkMenuBar();
+ searchPanel = new SearchPanel();
+ menuLeft.setStyleName("gerrit-topmenu-menuLeft");
+ menuLine.setStyleName("gerrit-topmenu");
+ menuArea.add(menuLine);
+ final FlowPanel menuRightPanel = new FlowPanel();
+ menuRightPanel.setStyleName("gerrit-topmenu-menuRight");
+ menuRightPanel.add(menuRight);
+ menuRightPanel.add(searchPanel);
+ menuLine.setWidget(0, 0, menuLeft);
+ menuLine.setWidget(0, 1, new FlowPanel());
+ menuLine.setWidget(0, 2, menuRightPanel);
+ final CellFormatter fmt = menuLine.getCellFormatter();
+ fmt.setStyleName(0, 0, "gerrit-topmenu-TDmenu");
+ fmt.setStyleName(0, 1, "gerrit-topmenu-TDglue");
+ fmt.setStyleName(0, 2, "gerrit-topmenu-TDmenu");
+
+ siteHeader = RootPanel.get("gerrit_header");
+ siteFooter = RootPanel.get("gerrit_footer");
+
+ body = new ViewSite<Screen>() {
+ @Override
+ protected void onShowView(Screen view) {
+ super.onShowView(view);
+ view.onShowView();
+ }
+ };
+ RootPanel.get("gerrit_body").add(body);
+
+ final RpcStatus rpcStatus = new RpcStatus(menuArea);
+ JsonUtil.addRpcStartHandler(rpcStatus);
+ JsonUtil.addRpcCompleteHandler(rpcStatus);
+ JsonUtil.setDefaultXsrfManager(new XsrfManager() {
+ @Override
+ public String getToken(JsonDefTarget proxy) {
+ return Cookies.getCookie("GerritAccount");
+ }
+
+ @Override
+ public void setToken(JsonDefTarget proxy, String token) {
+ // Ignore the request, we always rely upon the cookie.
+ }
+ });
+
+ final HostPageDataService hpd = GWT.create(HostPageDataService.class);
+ hpd.load(new GerritCallback<HostPageData>() {
+ public void onSuccess(final HostPageData result) {
+ myConfig = result.config;
+ if (result.userAccount != null) {
+ myAccount = result.userAccount;
+ applyUserPreferences();
+ }
+ onModuleLoad2();
+ }
+ });
+ }
+
+ private static void initHostname() {
+ myHost = Location.getHostName();
+ final int d1 = myHost.indexOf('.');
+ if (d1 < 0) {
+ return;
+ }
+ final int d2 = myHost.indexOf('.', d1 + 1);
+ if (d2 >= 0) {
+ myHost = myHost.substring(0, d2);
+ }
+ }
+
+ private static ArrayList<JavaScriptObject> historyHooks;
+
+ private static native void initHistoryHooks()
+ /*-{ $wnd['gerrit_addHistoryHook'] = function(h) { @com.google.gerrit.client.Gerrit::addHistoryHook(Lcom/google/gwt/core/client/JavaScriptObject;)(h); }; }-*/;
+
+ static void addHistoryHook(final JavaScriptObject hook) {
+ if (historyHooks == null) {
+ historyHooks = new ArrayList<JavaScriptObject>();
+ History.addValueChangeHandler(new ValueChangeHandler<String>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<String> event) {
+ dispatchHistoryHooks(event.getValue());
+ }
+ });
+ }
+ historyHooks.add(hook);
+ }
+
+ private static native void callHistoryHook(JavaScriptObject hook, String url)
+ /*-{ hook(url); }-*/;
+
+ private static void dispatchHistoryHooks(final String historyToken) {
+ final String url = Location.getPath() + "#" + historyToken;
+ for (final JavaScriptObject hook : historyHooks) {
+ callHistoryHook(hook, url);
+ }
+ }
+
+ private static void populateBottomMenu() {
+ final RootPanel btmmenu = RootPanel.get("gerrit_btmmenu");
+
+ final Label keyHelp = new Label(C.keyHelp());
+ keyHelp.setStyleName("gerrit-keyhelp");
+ btmmenu.add(keyHelp);
+
+ final String vs = getVersion();
+ final HTML version = new HTML(M.poweredBy(vs));
+ version.setStyleName("gerrit-version");
+ btmmenu.add(version);
+ }
+
+ /** @return version number of the Gerrit application software */
+ public static String getVersion() {
+ if (myVersion == null) {
+ if (GWT.isScript()) {
+ final Version v = GWT.create(Version.class);
+ myVersion = v.version();
+ } else {
+ myVersion = "dev";
+ }
+ }
+ return myVersion;
+ }
+
+ private void onModuleLoad2() {
+
+ refreshMenuBar();
+
+ final RootPanel sg = RootPanel.get("gerrit_startinggerrit");
+ sg.getElement().getParentElement().removeChild(sg.getElement());
+ RootPanel.detachNow(sg);
+
+ History.addValueChangeHandler(new HistoryHandler());
+ JumpKeys.register(body);
+
+ if ("".equals(History.getToken())) {
+ if (isSignedIn()) {
+ History.newItem(PageLinks.MINE);
+ } else {
+ History.newItem(PageLinks.ALL_OPEN);
+ }
+ } else {
+ History.fireCurrentHistoryState();
+ }
+ }
+
+ public static void refreshMenuBar() {
+ menuLeft.clear();
+ menuRight.clear();
+
+ final boolean signedIn = isSignedIn();
+ LinkMenuBar m;
+
+ m = new LinkMenuBar();
+ addLink(m, C.menuAllOpen(), PageLinks.ALL_OPEN);
+ addLink(m, C.menuAllMerged(), PageLinks.ALL_MERGED);
+ addLink(m, C.menuAllAbandoned(), PageLinks.ALL_ABANDONED);
+ menuLeft.add(m, C.menuAll());
+
+ if (signedIn) {
+ m = new LinkMenuBar();
+ addLink(m, C.menuMyChanges(), PageLinks.MINE);
+ addLink(m, C.menyMyDrafts(), PageLinks.MINE_DRAFTS);
+ addLink(m, C.menuMyStarredChanges(), PageLinks.MINE_STARRED);
+ menuLeft.add(m, C.menuMine());
+ menuLeft.selectTab(1);
+ } else {
+ menuLeft.selectTab(0);
+ }
+
+ if (signedIn) {
+ m = new LinkMenuBar();
+ addLink(m, C.menuGroups(), PageLinks.ADMIN_GROUPS);
+ addLink(m, C.menuProjects(), PageLinks.ADMIN_PROJECTS);
+ menuLeft.add(m, C.menuAdmin());
+ }
+
+ if (signedIn) {
+ whoAmI();
+ addLink(menuRight, C.menuSettings(), PageLinks.SETTINGS);
+ menuRight.add(anchor(C.menuSignOut(), "logout"));
+ } else {
+ switch (getConfig().getAuthType()) {
+ case HTTP:
+ case HTTP_LDAP:
+ break;
+
+ case OPENID:
+ menuRight.addItem(C.menuRegister(), new Command() {
+ public void execute() {
+ new OpenIdSignInDialog(SignInMode.REGISTER, null).center();
+ }
+ });
+ // fall through
+ case LDAP:
+ menuRight.addItem(C.menuSignIn(), new Command() {
+ public void execute() {
+ doSignIn();
+ }
+ });
+ break;
+
+ case DEVELOPMENT_BECOME_ANY_ACCOUNT:
+ menuRight.add(anchor("Become", "become"));
+ break;
+ }
+ }
+ }
+
+ public static void applyUserPreferences() {
+ final AccountGeneralPreferences p = myAccount.getGeneralPreferences();
+ CopyableLabel.setFlashEnabled(p.isUseFlashClipboard());
+ if (siteHeader != null) {
+ siteHeader.setVisible(p.isShowSiteHeader());
+ }
+ if (siteFooter != null) {
+ siteFooter.setVisible(p.isShowSiteHeader());
+ }
+ }
+
+ private static void whoAmI() {
+ final String name = FormatUtil.nameEmail(getUserAccount());
+ final InlineLabel l = new InlineLabel(name);
+ l.setStyleName("gerrit-MenuBarUserName");
+ menuRight.add(l);
+ }
+
+ private static Anchor anchor(final String text, final String to) {
+ final Anchor a = new Anchor(text, to);
+ a.setStyleName("gerrit-MenuItem");
+ Accessibility.setRole(a.getElement(), Accessibility.ROLE_MENUITEM);
+ return a;
+ }
+
+ private static void addLink(final LinkMenuBar m, final String text,
+ final String historyToken) {
+ m.addItem(new LinkMenuItem(text, historyToken));
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/GerritConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
index 7354042bab..7354042bab 100644
--- a/src/main/java/com/google/gerrit/client/GerritConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.java
diff --git a/src/main/java/com/google/gerrit/client/GerritConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
index 5d442629d8..5d442629d8 100644
--- a/src/main/java/com/google/gerrit/client/GerritConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/GerritIcons.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritIcons.java
index 442cd34371..442cd34371 100644
--- a/src/main/java/com/google/gerrit/client/GerritIcons.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritIcons.java
diff --git a/src/main/java/com/google/gerrit/client/GerritMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
index bfc663e979..bfc663e979 100644
--- a/src/main/java/com/google/gerrit/client/GerritMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.java
diff --git a/src/main/java/com/google/gerrit/client/GerritMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
index 159ddfcbbe..159ddfcbbe 100644
--- a/src/main/java/com/google/gerrit/client/GerritMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritMessages.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/HistoryHandler.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/HistoryHandler.java
new file mode 100644
index 0000000000..d238b4ad42
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/HistoryHandler.java
@@ -0,0 +1,292 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import static com.google.gerrit.common.PageLinks.ADMIN_GROUPS;
+import static com.google.gerrit.common.PageLinks.ADMIN_PROJECTS;
+import static com.google.gerrit.common.PageLinks.ALL_OPEN;
+import static com.google.gerrit.common.PageLinks.MINE;
+import static com.google.gerrit.common.PageLinks.MINE_DRAFTS;
+import static com.google.gerrit.common.PageLinks.MINE_STARRED;
+import static com.google.gerrit.common.PageLinks.REGISTER;
+import static com.google.gerrit.common.PageLinks.SETTINGS;
+import static com.google.gerrit.common.PageLinks.SETTINGS_NEW_AGREEMENT;
+import static com.google.gerrit.common.PageLinks.SETTINGS_WEBIDENT;
+
+import com.google.gerrit.client.account.AccountSettings;
+import com.google.gerrit.client.account.NewAgreementScreen;
+import com.google.gerrit.client.account.RegisterScreen;
+import com.google.gerrit.client.account.ValidateEmailScreen;
+import com.google.gerrit.client.admin.AccountGroupScreen;
+import com.google.gerrit.client.admin.GroupListScreen;
+import com.google.gerrit.client.admin.ProjectAdminScreen;
+import com.google.gerrit.client.admin.ProjectListScreen;
+import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
+import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
+import com.google.gerrit.client.changes.AccountDashboardScreen;
+import com.google.gerrit.client.changes.AllAbandonedChangesScreen;
+import com.google.gerrit.client.changes.AllMergedChangesScreen;
+import com.google.gerrit.client.changes.AllOpenChangesScreen;
+import com.google.gerrit.client.changes.ByProjectAbandonedChangesScreen;
+import com.google.gerrit.client.changes.ByProjectMergedChangesScreen;
+import com.google.gerrit.client.changes.ByProjectOpenChangesScreen;
+import com.google.gerrit.client.changes.ChangeQueryResultsScreen;
+import com.google.gerrit.client.changes.ChangeScreen;
+import com.google.gerrit.client.changes.MineDraftsScreen;
+import com.google.gerrit.client.changes.MineStarredScreen;
+import com.google.gerrit.client.changes.PublishCommentScreen;
+import com.google.gerrit.client.patches.PatchScreen;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.Change.Status;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwtorm.client.KeyUtil;
+
+public class HistoryHandler implements ValueChangeHandler<String> {
+ public static String toPatchSideBySide(final Patch.Key id) {
+ return toPatch("sidebyside", id);
+ }
+
+ public static String toPatchUnified(final Patch.Key id) {
+ return toPatch("unified", id);
+ }
+
+ public static String toPatch(final String type, final Patch.Key id) {
+ return "patch," + type + "," + id.toString();
+ }
+
+ public static String toAccountGroup(final AccountGroup.Id id) {
+ return "admin,group," + id.toString();
+ }
+
+ public static String toProjectAdmin(final Project.NameKey n, final String tab) {
+ return "admin,project," + n.toString() + "," + tab;
+ }
+
+ public static String toProject(final Project.NameKey proj, Status status) {
+ switch (status) {
+ case ABANDONED:
+ return "project,abandoned," + proj.toString() + ",n,z";
+
+ case MERGED:
+ return "project,merged," + proj.toString() + ",n,z";
+
+ case NEW:
+ case SUBMITTED:
+ default:
+ return "project,open," + proj.toString() + ",n,z";
+ }
+ }
+
+ @Override
+ public void onValueChange(final ValueChangeEvent<String> event) {
+ final String token = event.getValue();
+ Screen s;
+ try {
+ s = select(token);
+ } catch (RuntimeException err) {
+ GWT.log("Error parsing history token: " + token, err);
+ s = null;
+ }
+
+ if (s != null) {
+ Gerrit.display(s);
+ } else {
+ Gerrit.display(new NotFoundScreen());
+ }
+ }
+
+ private Screen select(final String token) {
+ String p;
+
+ if (token == null) {
+ return null;
+ }
+
+ if (SETTINGS.equals(token) || token.startsWith("settings,")) {
+ if (SETTINGS_NEW_AGREEMENT.equals(token)) {
+ return new NewAgreementScreen();
+ }
+ p = SETTINGS_NEW_AGREEMENT + ",";
+ if (token.startsWith(p)) {
+ return new NewAgreementScreen(skip(p, token));
+ }
+ return new AccountSettings(token);
+ }
+
+ if (MINE.equals(token)) {
+ if (Gerrit.isSignedIn()) {
+ return new AccountDashboardScreen(Gerrit.getUserAccount().getId());
+ } else {
+ final Screen r = new AccountDashboardScreen(null);
+ r.setRequiresSignIn(true);
+ return r;
+ }
+ }
+ if (token.startsWith("mine,")) {
+ if (MINE_STARRED.equals(token)) {
+ return new MineStarredScreen();
+ }
+ if (MINE_DRAFTS.equals(token)) {
+ return new MineDraftsScreen();
+ }
+ }
+
+ if (token.startsWith("all,")) {
+ p = "all,abandoned,";
+ if (token.startsWith(p)) {
+ return new AllAbandonedChangesScreen(skip(p, token));
+ }
+
+ p = "all,merged,";
+ if (token.startsWith(p)) {
+ return new AllMergedChangesScreen(skip(p, token));
+ }
+
+ p = "all,open,";
+ if (token.startsWith(p)) {
+ return new AllOpenChangesScreen(skip(p, token));
+ }
+ }
+
+ if (token.startsWith("project,")) {
+ p = "project,open,";
+ if (token.startsWith(p)) {
+ final String s = skip(p, token);
+ final int c = s.indexOf(',');
+ return new ByProjectOpenChangesScreen(Project.NameKey.parse(s
+ .substring(0, c)), s.substring(c + 1));
+ }
+
+ p = "project,merged,";
+ if (token.startsWith(p)) {
+ final String s = skip(p, token);
+ final int c = s.indexOf(',');
+ return new ByProjectMergedChangesScreen(Project.NameKey.parse(s
+ .substring(0, c)), s.substring(c + 1));
+ }
+
+ p = "project,abandoned,";
+ if (token.startsWith(p)) {
+ final String s = skip(p, token);
+ final int c = s.indexOf(',');
+ return new ByProjectAbandonedChangesScreen(Project.NameKey.parse(s
+ .substring(0, c)), s.substring(c + 1));
+ }
+ }
+
+ if (token.startsWith("patch,")) {
+ p = "patch,sidebyside,";
+ if (token.startsWith(p))
+ return new PatchScreen.SideBySide(Patch.Key.parse(skip(p, token)),
+ 0 /* patchIndex */, null /* patchTable */);
+
+ p = "patch,unified,";
+ if (token.startsWith(p))
+ return new PatchScreen.Unified(Patch.Key.parse(skip(p, token)),
+ 0 /* patchIndex */, null /* patchTable */);
+ }
+
+ p = "change,publish,";
+ if (token.startsWith(p))
+ return new PublishCommentScreen(PatchSet.Id.parse(skip(p, token)));
+
+ p = "change,";
+ if (token.startsWith(p))
+ return new ChangeScreen(Change.Id.parse(skip(p, token)));
+
+ p = "dashboard,";
+ if (token.startsWith(p))
+ return new AccountDashboardScreen(Account.Id.parse(skip(p, token)));
+
+ p = "q,";
+ if (token.startsWith(p)) {
+ final String s = skip(p, token);
+ final int c = s.indexOf(',');
+ return new ChangeQueryResultsScreen(s.substring(0, c), s.substring(c + 1));
+ }
+
+ if (token.startsWith("admin,")) {
+ p = "admin,group,";
+ if (token.startsWith(p))
+ return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
+
+ p = "admin,project,";
+ if (token.startsWith(p)) {
+ p = skip(p, token);
+ final int c = p.indexOf(',');
+ final String idstr = p.substring(0, c);
+ return new ProjectAdminScreen(Project.NameKey.parse(idstr), token);
+ }
+
+ if (ADMIN_GROUPS.equals(token)) {
+ return new GroupListScreen();
+ }
+
+ if (ADMIN_PROJECTS.equals(token)) {
+ return new ProjectListScreen();
+ }
+ }
+
+ p = REGISTER + ",";
+ if (token.startsWith(p)) {
+ return new RegisterScreen(skip(p, token));
+ } else if (REGISTER.equals(token)) {
+ return new RegisterScreen(MINE);
+ }
+
+ p = "VE,";
+ if (token.startsWith(p)) {
+ return new ValidateEmailScreen(skip(p, token));
+ }
+
+ p = "SignInFailure,";
+ if (token.startsWith(p)) {
+ final String[] args = skip(p, token).split(",");
+ final SignInMode mode = SignInMode.valueOf(args[0]);
+ final String msg = KeyUtil.decode(args[1]);
+ switch (Gerrit.getConfig().getAuthType()) {
+ case OPENID:
+ new OpenIdSignInDialog(mode, msg).center();
+ break;
+ case LDAP:
+ new UserPassSignInDialog(msg).center();
+ break;
+ default:
+ return null;
+ }
+ switch (mode) {
+ case SIGN_IN:
+ return select(ALL_OPEN);
+ case LINK_IDENTIY:
+ return new AccountSettings(SETTINGS_WEBIDENT);
+ }
+ }
+
+ return null;
+ }
+
+ private static String skip(final String prefix, final String in) {
+ return in.substring(prefix.length());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java
new file mode 100644
index 0000000000..cb3a7e639f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/HostPageDataService.java
@@ -0,0 +1,25 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.common.data.HostPageData;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.HostPageCache;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+
+interface HostPageDataService extends RemoteJsonService {
+ @HostPageCache(name = "gerrit_hostpagedata_obj", once = true)
+ void load(AsyncCallback<HostPageData> callback);
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java
new file mode 100644
index 0000000000..62f5b3baa9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/JumpKeys.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.globalkey.client.CompoundKeyCommand;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+import com.google.gwtexpui.globalkey.client.KeyCommandSet;
+
+class JumpKeys {
+ static void register(final Widget body) {
+ final KeyCommandSet jumps = new KeyCommandSet();
+
+ jumps.add(new KeyCommand(0, 'o', Gerrit.C.jumpAllOpen()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display(PageLinks.ALL_OPEN, true);
+ }
+ });
+ jumps.add(new KeyCommand(0, 'm', Gerrit.C.jumpAllMerged()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display(PageLinks.ALL_MERGED, true);
+ }
+ });
+
+ if (Gerrit.isSignedIn()) {
+ jumps.add(new KeyCommand(0, 'i', Gerrit.C.jumpMine()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display(PageLinks.MINE, true);
+ }
+ });
+ jumps.add(new KeyCommand(0, 'd', Gerrit.C.jumpMineDrafts()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display(PageLinks.MINE_DRAFTS, true);
+ }
+ });
+ jumps.add(new KeyCommand(0, 's', Gerrit.C.jumpMineStarred()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display(PageLinks.MINE_STARRED, true);
+ }
+ });
+ }
+
+ final KeyCommandSet jumping = new KeyCommandSet(Gerrit.C.sectionJumping());
+ jumping.add(new CompoundKeyCommand(0, 'g', "", jumps));
+ GlobalKey.add(body, jumping);
+ }
+
+ private JumpKeys() {
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/NotFoundScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java
index 29bbf85c8d..29bbf85c8d 100644
--- a/src/main/java/com/google/gerrit/client/NotFoundScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotFoundScreen.java
diff --git a/src/main/java/com/google/gerrit/client/NotSignedInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
index 98dadb44f1..98dadb44f1 100644
--- a/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/NotSignedInDialog.java
diff --git a/src/main/java/com/google/gerrit/client/RpcStatus.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
index 783e84b5aa..783e84b5aa 100644
--- a/src/main/java/com/google/gerrit/client/RpcStatus.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RpcStatus.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
new file mode 100644
index 0000000000..d61b9614d1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SearchPanel.java
@@ -0,0 +1,145 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.client.changes.ChangeScreen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+class SearchPanel extends Composite {
+ private final NpTextBox searchBox;
+ private HandlerRegistration regFocus;
+
+ SearchPanel() {
+ final FlowPanel body = new FlowPanel();
+ initWidget(body);
+ setStyleName("gerrit-SearchPanel");
+
+ searchBox = new NpTextBox();
+ searchBox.setVisibleLength(46);
+ searchBox.setText(Gerrit.C.searchHint());
+ searchBox.addStyleName("gerrit-InputFieldTypeHint");
+ searchBox.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (Gerrit.C.searchHint().equals(searchBox.getText())) {
+ searchBox.setText("");
+ searchBox.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ searchBox.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if ("".equals(searchBox.getText())) {
+ searchBox.setText(Gerrit.C.searchHint());
+ searchBox.addStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ searchBox.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ switch (event.getCharCode()) {
+ case KeyCodes.KEY_ENTER:
+ doSearch();
+ break;
+ case KeyCodes.KEY_ESCAPE:
+ searchBox.setText("");
+ searchBox.setFocus(false);
+ break;
+ }
+ }
+ });
+
+ final Button searchButton = new Button(Gerrit.C.searchButton());
+ searchButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ doSearch();
+ }
+ });
+
+ body.add(searchBox);
+ body.add(searchButton);
+ }
+
+ void setText(final String query) {
+ if (query == null || query.equals("")) {
+ searchBox.setText(Gerrit.C.searchHint());
+ searchBox.addStyleName("gerrit-InputFieldTypeHint");
+ } else {
+ searchBox.setText(query);
+ searchBox.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ if (regFocus == null) {
+ regFocus =
+ GlobalKey.addApplication(this, new KeyCommand(0, '/', Gerrit.C
+ .keySearch()) {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ event.preventDefault();
+ searchBox.setFocus(true);
+ searchBox.selectAll();
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onUnload() {
+ if (regFocus != null) {
+ regFocus.removeHandler();
+ regFocus = null;
+ }
+ }
+
+ private void doSearch() {
+ final String query = searchBox.getText().trim();
+ if (query.length() == 0 || Gerrit.C.searchHint().equals(query)) {
+ return;
+ }
+
+ searchBox.setFocus(false);
+
+ if (query.matches("^[1-9][0-9]*$")) {
+ final Change.Id ck = Change.Id.parse(query);
+ Gerrit.display(PageLinks.toChange(ck), new ChangeScreen(ck));
+ } else {
+ Gerrit.display(PageLinks.toChangeQuery(query), true);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/SignInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SignInDialog.java
new file mode 100644
index 0000000000..fb6f9ea1eb
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/SignInDialog.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client;
+
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+
+/** Prompts the user to sign in to their account. */
+public abstract class SignInDialog extends AutoCenterDialogBox {
+ protected final SignInMode mode;
+
+ /**
+ * Create a new dialog to handle user sign in.
+ *
+ * @param signInMode type of mode the login will perform.
+ */
+ protected SignInDialog(final SignInMode signInMode) {
+ super(/* auto hide */true, /* modal */true);
+ mode = signInMode;
+
+ switch (signInMode) {
+ case LINK_IDENTIY:
+ setText(Gerrit.C.linkIdentityDialogTitle());
+ break;
+ case REGISTER:
+ setText(Gerrit.C.registerDialogTitle());
+ break;
+ default:
+ setText(Gerrit.C.signInDialogTitle());
+ break;
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index 790899ba6f..790899ba6f 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
diff --git a/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index 88585c2aad..88585c2aad 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/account/AccountMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
index 2014f19596..2014f19596 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.java
diff --git a/src/main/java/com/google/gerrit/client/account/AccountMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
index d013911111..d013911111 100644
--- a/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountMessages.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountSettings.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountSettings.java
new file mode 100644
index 0000000000..e6aa43009d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountSettings.java
@@ -0,0 +1,168 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import static com.google.gerrit.client.FormatUtil.mediumFormat;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.LazyPanel;
+import com.google.gwt.user.client.ui.TabPanel;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountSettings extends AccountScreen {
+ private final String initialTabToken;
+ private int labelIdx, fieldIdx;
+ private Grid info;
+
+ private List<String> tabTokens;
+ private TabPanel tabs;
+
+ public AccountSettings(final String tabToken) {
+ initialTabToken = tabToken;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+
+ final int idx = tabTokens.indexOf(initialTabToken);
+ tabs.selectTab(0 <= idx ? idx : 0);
+ display(Gerrit.getUserAccount());
+ display();
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.accountSettingsHeading());
+
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ labelIdx = 1;
+ fieldIdx = 0;
+ } else {
+ labelIdx = 0;
+ fieldIdx = 1;
+ }
+
+ info = new Grid(4, 2);
+ info.setStyleName("gerrit-InfoBlock");
+ info.addStyleName("gerrit-AccountInfoBlock");
+ add(info);
+
+ infoRow(0, Util.C.fullName());
+ infoRow(1, Util.C.preferredEmail());
+ infoRow(2, Util.C.registeredOn());
+ infoRow(3, Util.C.accountId());
+
+ final CellFormatter fmt = info.getCellFormatter();
+ fmt.addStyleName(0, 0, "topmost");
+ fmt.addStyleName(0, 1, "topmost");
+ fmt.addStyleName(3, 0, "bottomheader");
+
+ tabTokens = new ArrayList<String>();
+ tabs = new TabPanel();
+ tabs.setWidth("98%");
+ add(tabs);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected PreferencePanel createWidget() {
+ return new PreferencePanel();
+ }
+ }, Util.C.tabPreferences());
+ tabTokens.add(PageLinks.SETTINGS);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ProjectWatchPanel createWidget() {
+ return new ProjectWatchPanel();
+ }
+ }, Util.C.watchedProjects());
+ tabTokens.add(PageLinks.SETTINGS_PROJECTS);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ContactPanelFull createWidget() {
+ final ContactPanelFull p = new ContactPanelFull();
+ p.accountSettings = AccountSettings.this;
+ return p;
+ }
+ }, Util.C.tabContactInformation());
+ tabTokens.add(PageLinks.SETTINGS_CONTACT);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected SshPanel createWidget() {
+ return new SshPanel();
+ }
+ }, Util.C.tabSshKeys());
+ tabTokens.add(PageLinks.SETTINGS_SSHKEYS);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ExternalIdPanel createWidget() {
+ return new ExternalIdPanel();
+ }
+ }, Util.C.tabWebIdentities());
+ tabTokens.add(PageLinks.SETTINGS_WEBIDENT);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected MyGroupsPanel createWidget() {
+ return new MyGroupsPanel();
+ }
+ }, Util.C.tabMyGroups());
+ tabTokens.add(PageLinks.SETTINGS_MYGROUPS);
+
+ if (Gerrit.getConfig().isUseContributorAgreements()) {
+ tabs.add(new LazyPanel() {
+ @Override
+ protected AgreementPanel createWidget() {
+ return new AgreementPanel();
+ }
+ }, Util.C.tabAgreements());
+ tabTokens.add(PageLinks.SETTINGS_AGREEMENTS);
+ }
+
+ tabs.addSelectionHandler(new SelectionHandler<Integer>() {
+ @Override
+ public void onSelection(final SelectionEvent<Integer> event) {
+ Gerrit.display(tabTokens.get(event.getSelectedItem()), false);
+ }
+ });
+ }
+
+ private void infoRow(final int row, final String name) {
+ info.setText(row, labelIdx, name);
+ info.getCellFormatter().addStyleName(row, 0, "header");
+ }
+
+ void display(final Account account) {
+ info.setText(0, fieldIdx, account.getFullName());
+ info.setText(1, fieldIdx, account.getPreferredEmail());
+ info.setText(2, fieldIdx, mediumFormat(account.getRegisteredOn()));
+ info.setText(3, fieldIdx, account.getId().toString());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AgreementPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
new file mode 100644
index 0000000000..315a24b37c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
@@ -0,0 +1,139 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AgreementInfo;
+import com.google.gerrit.reviewdb.AbstractAgreement;
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountGroupAgreement;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Hyperlink;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+class AgreementPanel extends Composite {
+ private AgreementTable agreements;
+
+ AgreementPanel() {
+ final FlowPanel body = new FlowPanel();
+
+ agreements = new AgreementTable();
+ body.add(agreements);
+ body.add(new Hyperlink(Util.C.newAgreement(), PageLinks.SETTINGS_NEW_AGREEMENT));
+
+ initWidget(body);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
+ public void onSuccess(final AgreementInfo result) {
+ agreements.display(result);
+ }
+ });
+ }
+
+ private class AgreementTable extends FancyFlexTable<AbstractAgreement> {
+ AgreementTable() {
+ table.setWidth("");
+ table.setText(0, 1, Util.C.agreementStatus());
+ table.setText(0, 2, Util.C.agreementName());
+ table.setText(0, 3, Util.C.agreementDescription());
+ table.setText(0, 4, Util.C.agreementAccepted());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ for (int c = 1; c <= 4; c++) {
+ fmt.addStyleName(0, c, S_DATA_HEADER);
+ }
+ }
+
+ void display(final AgreementInfo result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountAgreement k : result.userAccepted) {
+ addOne(result, k);
+ }
+ for (final AccountGroupAgreement k : result.groupAccepted) {
+ addOne(result, k);
+ }
+ }
+
+ void addOne(final AgreementInfo info, final AbstractAgreement k) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+
+ final ContributorAgreement cla = info.agreements.get(k.getAgreementId());
+ final String statusName;
+ if (cla == null || !cla.isActive()) {
+ statusName = Util.C.agreementStatus_EXPIRED();
+ } else {
+ switch (k.getStatus()) {
+ case NEW:
+ statusName = Util.C.agreementStatus_NEW();
+ break;
+ case REJECTED:
+ statusName = Util.C.agreementStatus_REJECTED();
+ break;
+ case VERIFIED:
+ statusName = Util.C.agreementStatus_VERIFIED();
+ break;
+ default:
+ statusName = k.getStatus().name();
+ }
+ }
+ table.setText(row, 1, statusName);
+
+ if (cla == null) {
+ table.setText(row, 2, "");
+ table.setText(row, 3, "");
+ } else {
+ final String url = cla.getAgreementUrl();
+ if (url != null && url.length() > 0) {
+ final Anchor a = new Anchor(cla.getShortName(), url);
+ a.setTarget("_blank");
+ table.setWidget(row, 2, a);
+ } else {
+ table.setText(row, 2, cla.getShortName());
+ }
+ table.setText(row, 3, cla.getShortDescription());
+ }
+
+ final SafeHtmlBuilder b = new SafeHtmlBuilder();
+ b.append(FormatUtil.mediumFormat(k.getAcceptedOn()));
+ b.br();
+ b.append(FormatUtil.mediumFormat(k.getReviewedOn()));
+ SafeHtml.set(table, row, 4, b);
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ for (int c = 1; c <= 4; c++) {
+ fmt.addStyleName(row, c, S_DATA_CELL);
+ }
+ fmt.addStyleName(row, 4, "C_LAST_UPDATE");
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
new file mode 100644
index 0000000000..16f23b5e7b
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
@@ -0,0 +1,130 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ContactInformation;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+class ContactPanelFull extends ContactPanelShort {
+ private Label hasContact;
+ private NpTextArea addressTxt;
+ private NpTextBox countryTxt;
+ private NpTextBox phoneTxt;
+ private NpTextBox faxTxt;
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+
+ addressTxt = new NpTextArea();
+ addressTxt.setVisibleLines(4);
+ addressTxt.setCharacterWidth(60);
+
+ countryTxt = new NpTextBox();
+ countryTxt.setVisibleLength(40);
+ countryTxt.setMaxLength(40);
+
+ phoneTxt = new NpTextBox();
+ phoneTxt.setVisibleLength(30);
+ phoneTxt.setMaxLength(30);
+
+ faxTxt = new NpTextBox();
+ faxTxt.setVisibleLength(30);
+ faxTxt.setMaxLength(30);
+
+ final Grid infoSecure = new Grid(4, 2);
+ infoSecure.setStyleName("gerrit-InfoBlock");
+ infoSecure.addStyleName("gerrit-AccountInfoBlock");
+
+ final HTML privhtml = new HTML(Util.C.contactPrivacyDetailsHtml());
+ privhtml.setStyleName("gerrit-AccountContactPrivacyDetails");
+
+ hasContact = new Label();
+ hasContact.setStyleName("gerrit-AccountContactOnFile");
+ hasContact.setVisible(false);
+
+ if (Gerrit.getConfig().isUseContactInfo()) {
+ body.add(privhtml);
+ body.add(hasContact);
+ body.add(infoSecure);
+ }
+
+ row(infoSecure, 0, Util.C.contactFieldAddress(), addressTxt);
+ row(infoSecure, 1, Util.C.contactFieldCountry(), countryTxt);
+ row(infoSecure, 2, Util.C.contactFieldPhone(), phoneTxt);
+ row(infoSecure, 3, Util.C.contactFieldFax(), faxTxt);
+
+ infoSecure.getCellFormatter().addStyleName(0, 0, "topmost");
+ infoSecure.getCellFormatter().addStyleName(0, 1, "topmost");
+ infoSecure.getCellFormatter().addStyleName(3, 0, "bottomheader");
+
+ final TextSaveButtonListener sbl = new TextSaveButtonListener(save);
+ addressTxt.addKeyPressHandler(sbl);
+ countryTxt.addKeyPressHandler(sbl);
+ phoneTxt.addKeyPressHandler(sbl);
+ faxTxt.addKeyPressHandler(sbl);
+ }
+
+ @Override
+ protected void display(final Account userAccount) {
+ super.display(userAccount);
+ displayHasContact(userAccount);
+ addressTxt.setText("");
+ countryTxt.setText("");
+ phoneTxt.setText("");
+ faxTxt.setText("");
+ }
+
+ private void displayHasContact(final Account userAccount) {
+ if (userAccount.isContactFiled()) {
+ final Timestamp dt = userAccount.getContactFiledOn();
+ hasContact.setText(Util.M.contactOnFile(new Date(dt.getTime())));
+ hasContact.setVisible(true);
+ } else {
+ hasContact.setVisible(false);
+ }
+ }
+
+ @Override
+ void onSaveSuccess(final Account userAccount) {
+ super.onSaveSuccess(userAccount);
+ displayHasContact(userAccount);
+ }
+
+ @Override
+ ContactInformation toContactInformation() {
+ final ContactInformation info;
+ if (Gerrit.getConfig().isUseContactInfo()) {
+ info = new ContactInformation();
+ info.setAddress(addressTxt.getText());
+ info.setCountry(countryTxt.getText());
+ info.setPhoneNumber(phoneTxt.getText());
+ info.setFaxNumber(faxTxt.getText());
+ } else {
+ info = null;
+ }
+ return info;
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
new file mode 100644
index 0000000000..f6ca6b247c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -0,0 +1,353 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ContactInformation;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class ContactPanelShort extends Composite {
+ AccountSettings accountSettings;
+
+ protected final FlowPanel body;
+ protected int labelIdx, fieldIdx;
+ protected Button save;
+
+ private String currentEmail;
+ protected boolean haveAccount;
+ private boolean haveEmails;
+
+ NpTextBox nameTxt;
+ private ListBox emailPick;
+ private Button registerNewEmail;
+
+ ContactPanelShort() {
+ body = new FlowPanel();
+ initWidget(body);
+ }
+
+ protected void onInitUI() {
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ labelIdx = 1;
+ fieldIdx = 0;
+ } else {
+ labelIdx = 0;
+ fieldIdx = 1;
+ }
+
+ nameTxt = new NpTextBox();
+ nameTxt.setVisibleLength(60);
+ nameTxt.setReadOnly(!canEditFullName());
+
+ emailPick = new ListBox();
+
+ final Grid infoPlainText = new Grid(2, 2);
+ infoPlainText.setStyleName("gerrit-InfoBlock");
+ infoPlainText.addStyleName("gerrit-AccountInfoBlock");
+
+ body.add(infoPlainText);
+
+ registerNewEmail = new Button(Util.C.buttonOpenRegisterNewEmail());
+ registerNewEmail.setEnabled(false);
+ registerNewEmail.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doRegisterNewEmail();
+ }
+ });
+ final FlowPanel emailLine = new FlowPanel();
+ emailLine.add(emailPick);
+ if (canRegisterNewEmail()) {
+ emailLine.add(registerNewEmail);
+ }
+
+ row(infoPlainText, 0, Util.C.contactFieldFullName(), nameTxt);
+ row(infoPlainText, 1, Util.C.contactFieldEmail(), emailLine);
+
+ infoPlainText.getCellFormatter().addStyleName(0, 0, "topmost");
+ infoPlainText.getCellFormatter().addStyleName(0, 1, "topmost");
+ infoPlainText.getCellFormatter().addStyleName(1, 0, "bottomheader");
+
+ save = new Button(Util.C.buttonSaveChanges());
+ save.setEnabled(false);
+ save.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doSave();
+ }
+ });
+
+ final TextSaveButtonListener sbl = new TextSaveButtonListener(save);
+ nameTxt.addKeyPressHandler(sbl);
+ emailPick.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(final ChangeEvent event) {
+ final int idx = emailPick.getSelectedIndex();
+ final String v = 0 <= idx ? emailPick.getValue(idx) : null;
+ if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
+ for (int i = 0; i < emailPick.getItemCount(); i++) {
+ if (currentEmail.equals(emailPick.getValue(i))) {
+ emailPick.setSelectedIndex(i);
+ break;
+ }
+ }
+ doRegisterNewEmail();
+ } else {
+ save.setEnabled(true);
+ }
+ }
+ });
+ }
+
+ private boolean canEditFullName() {
+ return Gerrit.getConfig().canEdit(Account.FieldName.FULL_NAME);
+ }
+
+ private boolean canRegisterNewEmail() {
+ return Gerrit.getConfig().canEdit(Account.FieldName.REGISTER_NEW_EMAIL);
+ }
+
+ void hideSaveButton() {
+ save.setVisible(false);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+
+ onInitUI();
+ body.add(save);
+ display(Gerrit.getUserAccount());
+
+ emailPick.clear();
+ emailPick.setEnabled(false);
+ registerNewEmail.setEnabled(false);
+
+ haveAccount = false;
+ haveEmails = false;
+
+ Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
+ public void onSuccess(final Account result) {
+ if (!isAttached()) {
+ return;
+ }
+ display(result);
+ haveAccount = true;
+ postLoad();
+ }
+ });
+ Util.ACCOUNT_SEC
+ .myExternalIds(new GerritCallback<List<AccountExternalId>>() {
+ public void onSuccess(final List<AccountExternalId> result) {
+ if (!isAttached()) {
+ return;
+ }
+ final Set<String> emails = new HashSet<String>();
+ for (final AccountExternalId i : result) {
+ if (i.getEmailAddress() != null
+ && i.getEmailAddress().length() > 0) {
+ emails.add(i.getEmailAddress());
+ }
+ }
+ final List<String> addrs = new ArrayList<String>(emails);
+ Collections.sort(addrs);
+ for (String s : addrs) {
+ emailPick.addItem(s);
+ }
+ haveEmails = true;
+ postLoad();
+ }
+ });
+ }
+
+ private void postLoad() {
+ if (haveAccount && haveEmails) {
+ if (currentEmail != null) {
+ boolean found = false;
+ for (int i = 0; i < emailPick.getItemCount(); i++) {
+ if (currentEmail.equals(emailPick.getValue(i))) {
+ emailPick.setSelectedIndex(i);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ emailPick.addItem(currentEmail);
+ emailPick.setSelectedIndex(emailPick.getItemCount() - 1);
+ }
+ }
+ if (emailPick.getItemCount() > 0) {
+ emailPick.setVisible(true);
+ emailPick.setEnabled(true);
+ if (canRegisterNewEmail()) {
+ final String t = Util.C.buttonOpenRegisterNewEmail();
+ emailPick.addItem("... " + t + " ", t);
+ }
+ } else {
+ emailPick.setVisible(false);
+ }
+ registerNewEmail.setEnabled(true);
+ }
+ }
+
+ protected void row(final Grid info, final int row, final String name,
+ final Widget field) {
+ info.setText(row, labelIdx, name);
+ info.setWidget(row, fieldIdx, field);
+ info.getCellFormatter().addStyleName(row, 0, "header");
+ }
+
+ protected void display(final Account userAccount) {
+ currentEmail = userAccount.getPreferredEmail();
+ nameTxt.setText(userAccount.getFullName());
+ save.setEnabled(false);
+ }
+
+ private void doRegisterNewEmail() {
+ if (!canRegisterNewEmail()) {
+ return;
+ }
+
+ final AutoCenterDialogBox box = new AutoCenterDialogBox(true, true);
+ final VerticalPanel body = new VerticalPanel();
+
+ final NpTextBox inEmail = new NpTextBox();
+ inEmail.setVisibleLength(60);
+
+ final Button register = new Button(Util.C.buttonSendRegisterNewEmail());
+ final FormPanel form = new FormPanel();
+ form.addSubmitHandler(new FormPanel.SubmitHandler() {
+ @Override
+ public void onSubmit(final SubmitEvent event) {
+ event.cancel();
+ final String addr = inEmail.getText().trim();
+ if (!addr.contains("@")) {
+ return;
+ }
+
+ inEmail.setEnabled(false);
+ register.setEnabled(false);
+ Util.ACCOUNT_SEC.registerEmail(addr, new GerritCallback<VoidResult>() {
+ public void onSuccess(VoidResult result) {
+ box.hide();
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ inEmail.setEnabled(true);
+ register.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ form.setWidget(body);
+
+ register.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ form.submit();
+ }
+ });
+ body.add(new HTML(Util.C.descRegisterNewEmail()));
+ body.add(inEmail);
+ body.add(register);
+
+ box.setText(Util.C.titleRegisterNewEmail());
+ box.setWidget(form);
+ box.center();
+ inEmail.setFocus(true);
+ }
+
+ void doSave() {
+ String newName = canEditFullName() ? nameTxt.getText() : null;
+ if ("".equals(newName)) {
+ newName = null;
+ }
+
+ final String newEmail;
+ if (emailPick.isEnabled() && emailPick.getSelectedIndex() >= 0) {
+ final String v = emailPick.getValue(emailPick.getSelectedIndex());
+ if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
+ newEmail = currentEmail;
+ } else {
+ newEmail = v;
+ }
+ } else {
+ newEmail = currentEmail;
+ }
+
+ final ContactInformation info = toContactInformation();
+ save.setEnabled(false);
+ registerNewEmail.setEnabled(false);
+
+ Util.ACCOUNT_SEC.updateContact(newName, newEmail, info,
+ new GerritCallback<Account>() {
+ public void onSuccess(final Account result) {
+ registerNewEmail.setEnabled(true);
+ onSaveSuccess(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ save.setEnabled(true);
+ registerNewEmail.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ void onSaveSuccess(final Account result) {
+ final Account me = Gerrit.getUserAccount();
+ me.setFullName(result.getFullName());
+ me.setPreferredEmail(result.getPreferredEmail());
+ me.setSshUserName(result.getSshUserName());
+ Gerrit.refreshMenuBar();
+ if (accountSettings != null) {
+ accountSettings.display(me);
+ }
+ }
+
+ ContactInformation toContactInformation() {
+ return null;
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java
new file mode 100644
index 0000000000..fc3d9443ef
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java
@@ -0,0 +1,243 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
+import com.google.gerrit.client.auth.openid.OpenIdUtil;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class ExternalIdPanel extends Composite {
+ private IdTable identites;
+ private Button deleteIdentity;
+
+ ExternalIdPanel() {
+ final FlowPanel body = new FlowPanel();
+
+ identites = new IdTable();
+ body.add(identites);
+
+ deleteIdentity = new Button(Util.C.buttonDeleteIdentity());
+ deleteIdentity.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ identites.deleteChecked();
+ }
+ });
+ body.add(deleteIdentity);
+
+ switch (Gerrit.getConfig().getAuthType()) {
+ case OPENID: {
+ final Button linkIdentity = new Button(Util.C.buttonLinkIdentity());
+ linkIdentity.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ new OpenIdSignInDialog(SignInMode.LINK_IDENTIY, null).center();
+ }
+ });
+ body.add(linkIdentity);
+ break;
+ }
+ }
+
+ initWidget(body);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ refresh();
+ }
+
+ private void refresh() {
+ Util.ACCOUNT_SEC
+ .myExternalIds(new GerritCallback<List<AccountExternalId>>() {
+ public void onSuccess(final List<AccountExternalId> result) {
+ identites.display(result);
+ }
+ });
+ }
+
+ private class IdTable extends FancyFlexTable<AccountExternalId> {
+ IdTable() {
+ table.setWidth("");
+ table.setText(0, 2, Util.C.webIdLastUsed());
+ table.setText(0, 3, Util.C.webIdStatus());
+ table.setText(0, 4, Util.C.webIdEmail());
+ table.setText(0, 5, Util.C.webIdIdentity());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ fmt.addStyleName(0, 4, S_DATA_HEADER);
+ fmt.addStyleName(0, 5, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<AccountExternalId.Key> keys =
+ new HashSet<AccountExternalId.Key>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final AccountExternalId k = getRowItem(row);
+ if (k == null) {
+ continue;
+ }
+ final CheckBox cb = (CheckBox) table.getWidget(row, 1);
+ if (cb == null) {
+ continue;
+ }
+ if (cb.getValue()) {
+ keys.add(k.getKey());
+ }
+ }
+ if (!keys.isEmpty()) {
+ deleteIdentity.setEnabled(false);
+ Util.ACCOUNT_SEC.deleteExternalIds(keys,
+ new GerritCallback<Set<AccountExternalId.Key>>() {
+ public void onSuccess(final Set<AccountExternalId.Key> removed) {
+ deleteIdentity.setEnabled(true);
+ for (int row = 1; row < table.getRowCount();) {
+ final AccountExternalId k = getRowItem(row);
+ if (k != null && removed.contains(k.getKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ deleteIdentity.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+ }
+
+ void display(final List<AccountExternalId> result) {
+ Collections.sort(result, new Comparator<AccountExternalId>() {
+ @Override
+ public int compare(AccountExternalId a, AccountExternalId b) {
+ return emailOf(a).compareTo(emailOf(b));
+ }
+
+ private String emailOf(final AccountExternalId a) {
+ return a.getEmailAddress() != null ? a.getEmailAddress() : "";
+ }
+ });
+
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountExternalId k : result) {
+ addOneId(k);
+ }
+
+ final AccountExternalId mostRecent = AccountExternalId.mostRecent(result);
+ if (mostRecent != null) {
+ for (int row = 1; row < table.getRowCount(); row++) {
+ if (getRowItem(row) == mostRecent) {
+ // Remove the box from the most recent row, this prevents
+ // the user from trying to delete the identity they last used
+ // to login, possibly locking themselves out of the account.
+ //
+ table.setHTML(row, 1, "&nbsp;");
+ break;
+ }
+ }
+ }
+ }
+
+ void addOneId(final AccountExternalId k) {
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+
+ table.setWidget(row, 1, new CheckBox());
+ if (k.getLastUsedOn() != null) {
+ table.setText(row, 2, FormatUtil.mediumFormat(k.getLastUsedOn()));
+ } else {
+ table.setText(row, 2, "");
+ }
+ if (k.isTrusted()) {
+ table.setText(row, 3, "");
+ } else {
+ table.setText(row, 3, Util.C.untrustedProvider());
+ fmt.addStyleName(row, 3, "gerrit-Identity-UntrustedExternalId");
+ }
+ if (k.getEmailAddress() != null && k.getEmailAddress().length() > 0) {
+ table.setText(row, 4, k.getEmailAddress());
+ } else {
+ table.setText(row, 4, "");
+ }
+ table.setText(row, 5, describe(k));
+
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+ fmt.addStyleName(row, 3, S_DATA_CELL);
+ fmt.addStyleName(row, 3, "C_LAST_UPDATE");
+ fmt.addStyleName(row, 4, S_DATA_CELL);
+ fmt.addStyleName(row, 5, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+
+ private String describe(final AccountExternalId k) {
+ if (k.isScheme(AccountExternalId.SCHEME_GERRIT)) {
+ // A local user identity should just be itself.
+ //
+ return k.getSchemeRest(AccountExternalId.SCHEME_GERRIT);
+
+ } else if (k.isScheme(AccountExternalId.SCHEME_MAILTO)) {
+ // Describe a mailto address as just its email address, which
+ // is already shown in the email address field.
+ //
+ return "";
+
+ } else if (k.isScheme(OpenIdUrls.URL_GOOGLE)) {
+ return OpenIdUtil.C.nameGoogle();
+
+ } else if (k.isScheme(OpenIdUrls.URL_YAHOO)) {
+ return OpenIdUtil.C.nameYahoo();
+
+ } else if (k.isScheme(AccountExternalId.LEGACY_GAE)) {
+ return OpenIdUtil.C.nameGoogle() + " (Imported from Google AppEngine)";
+
+ } else {
+ return k.getExternalId();
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java
new file mode 100644
index 0000000000..9ba20e82bf
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.admin.GroupTable;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+
+import java.util.List;
+
+class MyGroupsPanel extends Composite {
+ private GroupTable groups;
+
+ MyGroupsPanel() {
+ final FlowPanel body = new FlowPanel();
+
+ groups = new GroupTable(false /* do not hyperlink to admin */);
+ body.add(groups);
+
+ initWidget(body);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ refresh();
+ }
+
+ private void refresh() {
+ Util.ACCOUNT_SEC.myGroups(new GerritCallback<List<AccountGroup>>() {
+ public void onSuccess(final List<AccountGroup> result) {
+ groups.display(result);
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
new file mode 100644
index 0000000000..9582103a73
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
@@ -0,0 +1,276 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.ErrorDialog;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AgreementInfo;
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountGroupAgreement;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.RadioButton;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class NewAgreementScreen extends AccountScreen {
+ private final String nextToken;
+ private Set<ContributorAgreement.Id> mySigned;
+ private List<ContributorAgreement> available;
+ private ContributorAgreement current;
+
+ private VerticalPanel radios;
+
+ private Panel agreementGroup;
+ private HTML agreementHtml;
+
+ private Panel contactGroup;
+ private ContactPanelFull contactPanel;
+
+ private Panel finalGroup;
+ private NpTextBox yesIAgreeBox;
+ private Button submit;
+
+ public NewAgreementScreen() {
+ this(null);
+ }
+
+ public NewAgreementScreen(final String token) {
+ nextToken = token != null ? token : PageLinks.SETTINGS_AGREEMENTS;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
+ public void onSuccess(AgreementInfo result) {
+ if (isAttached()) {
+ mySigned = new HashSet<ContributorAgreement.Id>();
+ for (AccountAgreement a : result.userAccepted) {
+ mySigned.add(a.getAgreementId());
+ }
+ for (AccountGroupAgreement a : result.groupAccepted) {
+ mySigned.add(a.getAgreementId());
+ }
+ postRPC();
+ }
+ }
+ });
+ Gerrit.SYSTEM_SVC
+ .contributorAgreements(new GerritCallback<List<ContributorAgreement>>() {
+ public void onSuccess(final List<ContributorAgreement> result) {
+ if (isAttached()) {
+ available = result;
+ postRPC();
+ }
+ }
+ });
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.newAgreement());
+
+ final FlowPanel formBody = new FlowPanel();
+ radios = new VerticalPanel();
+ formBody.add(radios);
+
+ agreementGroup = new FlowPanel();
+ agreementGroup
+ .add(new SmallHeading(Util.C.newAgreementReviewLegalHeading()));
+
+ agreementHtml = new HTML();
+ agreementHtml.setStyleName("gerrit-ContributorAgreement-Legal");
+ agreementGroup.add(agreementHtml);
+ formBody.add(agreementGroup);
+
+ contactGroup = new FlowPanel();
+ contactGroup
+ .add(new SmallHeading(Util.C.newAgreementReviewContactHeading()));
+ formBody.add(contactGroup);
+
+ finalGroup = new VerticalPanel();
+ finalGroup.add(new SmallHeading(Util.C.newAgreementCompleteHeading()));
+ final FlowPanel fp = new FlowPanel();
+ yesIAgreeBox = new NpTextBox();
+ yesIAgreeBox.setVisibleLength(Util.C.newAgreementIAGREE().length() + 8);
+ yesIAgreeBox.setMaxLength(Util.C.newAgreementIAGREE().length());
+ fp.add(yesIAgreeBox);
+ fp.add(new InlineLabel(Util.M.enterIAGREE(Util.C.newAgreementIAGREE())));
+ finalGroup.add(fp);
+ submit = new Button(Util.C.buttonSubmitNewAgreement());
+ submit.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doSign();
+ }
+ });
+ finalGroup.add(submit);
+ formBody.add(finalGroup);
+ new TextSaveButtonListener(yesIAgreeBox, submit);
+
+ final FormPanel form = new FormPanel();
+ form.add(formBody);
+ add(form);
+ }
+
+ private void postRPC() {
+ if (mySigned != null && available != null) {
+ renderSelf();
+ display();
+ }
+ }
+
+ private void renderSelf() {
+ current = null;
+ agreementGroup.setVisible(false);
+ contactGroup.setVisible(false);
+ finalGroup.setVisible(false);
+ radios.clear();
+
+ final SmallHeading hdr = new SmallHeading();
+ if (available.isEmpty()) {
+ hdr.setText(Util.C.newAgreementNoneAvailable());
+ } else {
+ hdr.setText(Util.C.newAgreementSelectTypeHeading());
+ }
+ radios.add(hdr);
+
+ for (final ContributorAgreement cla : available) {
+ final RadioButton r = new RadioButton("cla_id", cla.getShortName());
+ r.addStyleName("gerrit-ContributorAgreement-Button");
+ radios.add(r);
+
+ if (mySigned.contains(cla.getId())) {
+ r.setEnabled(false);
+ final Label l = new Label(Util.C.newAgreementAlreadySubmitted());
+ l.setStyleName("gerrit-ContributorAgreement-AlreadySubmitted");
+ radios.add(l);
+ } else {
+ r.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ showCLA(cla);
+ }
+ });
+ }
+
+ if (cla.getShortDescription() != null
+ && !cla.getShortDescription().equals("")) {
+ final Label l = new Label(cla.getShortDescription());
+ l.setStyleName("gerrit-ContributorAgreement-ShortDescription");
+ radios.add(l);
+ }
+ }
+ }
+
+ private void doSign() {
+ submit.setEnabled(false);
+
+ if (current == null
+ || !Util.C.newAgreementIAGREE()
+ .equalsIgnoreCase(yesIAgreeBox.getText())) {
+ yesIAgreeBox.setText("");
+ yesIAgreeBox.setFocus(true);
+ return;
+ }
+
+ if (contactGroup.isVisible()) {
+ contactPanel.doSave();
+ }
+ Util.ACCOUNT_SEC.enterAgreement(current.getId(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ Gerrit.display(nextToken, true);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ yesIAgreeBox.setText("");
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void showCLA(final ContributorAgreement cla) {
+ current = cla;
+ String url = cla.getAgreementUrl();
+ if (url != null && url.length() > 0) {
+ agreementGroup.setVisible(true);
+ agreementHtml.setText(Gerrit.C.rpcStatusLoading());
+ if (!url.startsWith("http:") && !url.startsWith("https:")) {
+ url = GWT.getHostPageBaseURL() + url;
+ }
+ final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
+ rb.setCallback(new RequestCallback() {
+ public void onError(Request request, Throwable exception) {
+ new ErrorDialog(exception).center();
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ final String ct = response.getHeader("Content-Type");
+ if (response.getStatusCode() == 200 && ct != null
+ && (ct.equals("text/html") || ct.startsWith("text/html;"))) {
+ agreementHtml.setHTML(response.getText());
+ } else {
+ new ErrorDialog(response.getStatusText()).center();
+ }
+ }
+ });
+ try {
+ rb.send();
+ } catch (RequestException e) {
+ new ErrorDialog(e).show();
+ }
+ } else {
+ agreementGroup.setVisible(false);
+ }
+
+ if (contactPanel == null && cla.isRequireContactInformation()) {
+ contactPanel = new ContactPanelFull();
+ contactGroup.add(contactPanel);
+ contactPanel.hideSaveButton();
+ }
+ contactGroup.setVisible(cla.isRequireContactInformation());
+ finalGroup.setVisible(true);
+ yesIAgreeBox.setText("");
+ submit.setEnabled(false);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/PreferencePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/PreferencePanel.java
new file mode 100644
index 0000000000..583aa2a8a2
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/PreferencePanel.java
@@ -0,0 +1,202 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.CONTEXT_CHOICES;
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.DEFAULT_CONTEXT;
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.DEFAULT_PAGESIZE;
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.PAGESIZE_CHOICES;
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+class PreferencePanel extends Composite {
+ private CheckBox showSiteHeader;
+ private CheckBox useFlashClipboard;
+ private ListBox defaultContext;
+ private ListBox maximumPageSize;
+ private Button save;
+
+ PreferencePanel() {
+ final FlowPanel body = new FlowPanel();
+
+ final ClickHandler onClickSave = new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ save.setEnabled(true);
+ }
+ };
+ final ChangeHandler onChangeSave = new ChangeHandler() {
+ @Override
+ public void onChange(final ChangeEvent event) {
+ save.setEnabled(true);
+ }
+ };
+
+ showSiteHeader = new CheckBox(Util.C.showSiteHeader());
+ showSiteHeader.addClickHandler(onClickSave);
+
+ useFlashClipboard = new CheckBox(Util.C.useFlashClipboard());
+ useFlashClipboard.addClickHandler(onClickSave);
+
+ maximumPageSize = new ListBox();
+ for (final short v : PAGESIZE_CHOICES) {
+ maximumPageSize.addItem(Util.M.rowsPerPage(v), String.valueOf(v));
+ }
+ maximumPageSize.addChangeHandler(onChangeSave);
+
+ defaultContext = new ListBox();
+ for (final short v : CONTEXT_CHOICES) {
+ final String label;
+ if (v == WHOLE_FILE_CONTEXT) {
+ label = Util.C.contextWholeFile();
+ } else {
+ label = Util.M.lines(v);
+ }
+ defaultContext.addItem(label, String.valueOf(v));
+ }
+ defaultContext.addChangeHandler(onChangeSave);
+
+ final int labelIdx, fieldIdx;
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ labelIdx = 1;
+ fieldIdx = 0;
+ } else {
+ labelIdx = 0;
+ fieldIdx = 1;
+ }
+ final Grid formGrid = new Grid(4, 2);
+
+ int row = 0;
+ formGrid.setText(row, labelIdx, "");
+ formGrid.setWidget(row, fieldIdx, showSiteHeader);
+ row++;
+
+ formGrid.setText(row, labelIdx, "");
+ formGrid.setWidget(row, fieldIdx, useFlashClipboard);
+ row++;
+
+ formGrid.setText(row, labelIdx, Util.C.maximumPageSizeFieldLabel());
+ formGrid.setWidget(row, fieldIdx, maximumPageSize);
+ row++;
+
+ formGrid.setText(row, labelIdx, Util.C.defaultContextFieldLabel());
+ formGrid.setWidget(row, fieldIdx, defaultContext);
+ row++;
+
+ body.add(formGrid);
+
+ save = new Button(Util.C.buttonSaveChanges());
+ save.setEnabled(false);
+ save.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doSave();
+ }
+ });
+ body.add(save);
+
+ initWidget(body);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
+ public void onSuccess(final Account result) {
+ display(result.getGeneralPreferences());
+ enable(true);
+ }
+ });
+ }
+
+ private void enable(final boolean on) {
+ showSiteHeader.setEnabled(on);
+ useFlashClipboard.setEnabled(on);
+ maximumPageSize.setEnabled(on);
+ defaultContext.setEnabled(on);
+ }
+
+ private void display(final AccountGeneralPreferences p) {
+ showSiteHeader.setValue(p.isShowSiteHeader());
+ useFlashClipboard.setValue(p.isUseFlashClipboard());
+ setListBox(maximumPageSize, DEFAULT_PAGESIZE, p.getMaximumPageSize());
+ setListBox(defaultContext, DEFAULT_CONTEXT, p.getDefaultContext());
+ }
+
+ private void setListBox(final ListBox f, final short defaultValue,
+ final short currentValue) {
+ final int n = f.getItemCount();
+ for (int i = 0; i < n; i++) {
+ if (Short.parseShort(f.getValue(i)) == currentValue) {
+ f.setSelectedIndex(i);
+ return;
+ }
+ }
+ if (currentValue != defaultValue) {
+ setListBox(f, defaultValue, defaultValue);
+ }
+ }
+
+ private short getListBox(final ListBox f, final short defaultValue) {
+ final int idx = f.getSelectedIndex();
+ if (0 <= idx) {
+ return Short.parseShort(f.getValue(idx));
+ }
+ return defaultValue;
+ }
+
+ private void doSave() {
+ final AccountGeneralPreferences p = new AccountGeneralPreferences();
+ p.setShowSiteHeader(showSiteHeader.getValue());
+ p.setUseFlashClipboard(useFlashClipboard.getValue());
+ p.setMaximumPageSize(getListBox(maximumPageSize, DEFAULT_PAGESIZE));
+ p.setDefaultContext(getListBox(defaultContext, DEFAULT_CONTEXT));
+
+ enable(false);
+ save.setEnabled(false);
+
+ Util.ACCOUNT_SVC.changePreferences(p, new GerritCallback<VoidResult>() {
+ @Override
+ public void onSuccess(final VoidResult result) {
+ Gerrit.getUserAccount().setGeneralPreferences(p);
+ Gerrit.applyUserPreferences();
+ enable(true);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ enable(true);
+ save.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
new file mode 100644
index 0000000000..8b674cea7a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
@@ -0,0 +1,313 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.client.ui.ProjectLink;
+import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
+import com.google.gerrit.common.data.AccountProjectWatchInfo;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.Change.Status;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.HashSet;
+import java.util.List;
+
+class ProjectWatchPanel extends Composite {
+ private WatchTable watches;
+
+ private Button addNew;
+ private SuggestBox nameTxt;
+ private Button delSel;
+
+ ProjectWatchPanel() {
+ final FlowPanel body = new FlowPanel();
+
+ {
+ final FlowPanel fp = new FlowPanel();
+ fp.setStyleName("gerrit-ProjectWatchPanel-AddPanel");
+
+ final NpTextBox box = new NpTextBox();
+ nameTxt = new SuggestBox(new ProjectNameSuggestOracle(), box);
+ box.setVisibleLength(50);
+ box.setText(Util.C.defaultProjectName());
+ box.addStyleName("gerrit-InputFieldTypeHint");
+ box.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (Util.C.defaultProjectName().equals(box.getText())) {
+ box.setText("");
+ box.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ box.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if ("".equals(box.getText())) {
+ box.setText(Util.C.defaultProjectName());
+ box.addStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ fp.add(nameTxt);
+
+ addNew = new Button(Util.C.buttonWatchProject());
+ addNew.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddNew();
+ }
+ });
+ fp.add(addNew);
+ body.add(fp);
+ }
+
+ watches = new WatchTable();
+ body.add(watches);
+ {
+ final FlowPanel fp = new FlowPanel();
+ delSel = new Button(Util.C.buttonDeleteSshKey());
+ delSel.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ watches.deleteChecked();
+ }
+ });
+ fp.add(delSel);
+ body.add(fp);
+ }
+
+ initWidget(body);
+ }
+
+ void doAddNew() {
+ final String projectName = nameTxt.getText();
+ if (projectName == null || projectName.length() == 0
+ || Util.C.defaultProjectName().equals(projectName)) {
+ return;
+ }
+
+ addNew.setEnabled(false);
+ Util.ACCOUNT_SVC.addProjectWatch(projectName,
+ new GerritCallback<AccountProjectWatchInfo>() {
+ public void onSuccess(final AccountProjectWatchInfo result) {
+ addNew.setEnabled(true);
+ nameTxt.setText("");
+ watches.insertWatch(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ addNew.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.ACCOUNT_SVC
+ .myProjectWatch(new GerritCallback<List<AccountProjectWatchInfo>>() {
+ public void onSuccess(final List<AccountProjectWatchInfo> result) {
+ watches.display(result);
+ }
+ });
+ }
+
+ private class WatchTable extends FancyFlexTable<AccountProjectWatchInfo> {
+ WatchTable() {
+ table.setWidth("");
+ table.insertRow(1);
+ table.setText(0, 2, com.google.gerrit.client.changes.Util.C
+ .changeTableColumnProject());
+ table.setText(0, 3, Util.C.watchedProjectColumnEmailNotifications());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ fmt.setRowSpan(0, 0, 2);
+ fmt.setRowSpan(0, 1, 2);
+ fmt.setRowSpan(0, 2, 2);
+ DOM.setElementProperty(fmt.getElement(0, 3), "align", "center");
+
+ fmt.setColSpan(0, 3, 3);
+ table.setText(1, 0, Util.C.watchedProjectColumnNewChanges());
+ table.setText(1, 1, Util.C.watchedProjectColumnAllComments());
+ table.setText(1, 2, Util.C.watchedProjectColumnSubmittedChanges());
+ fmt.addStyleName(1, 0, S_DATA_HEADER);
+ fmt.addStyleName(1, 1, S_DATA_HEADER);
+ fmt.addStyleName(1, 2, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<AccountProjectWatch.Key> ids =
+ new HashSet<AccountProjectWatch.Key>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final AccountProjectWatchInfo k = getRowItem(row);
+ if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
+ ids.add(k.getWatch().getKey());
+ }
+ }
+ if (!ids.isEmpty()) {
+ Util.ACCOUNT_SVC.deleteProjectWatches(ids,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ for (int row = 1; row < table.getRowCount();) {
+ final AccountProjectWatchInfo k = getRowItem(row);
+ if (k != null && ids.contains(k.getWatch().getKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ }
+ });
+ }
+ }
+
+ void insertWatch(final AccountProjectWatchInfo k) {
+ final String newName = k.getProject().getName();
+ int row = 1;
+ for (; row < table.getRowCount(); row++) {
+ final AccountProjectWatchInfo i = getRowItem(row);
+ if (i != null && i.getProject().getName().compareTo(newName) >= 0) {
+ break;
+ }
+ }
+
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+
+ void display(final List<AccountProjectWatchInfo> result) {
+ while (2 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountProjectWatchInfo k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final AccountProjectWatchInfo k) {
+ table.setWidget(row, 1, new CheckBox());
+ table.setWidget(row, 2, new ProjectLink(k.getProject().getNameKey(), Status.NEW));
+ {
+ final CheckBox notifyNewChanges = new CheckBox();
+ notifyNewChanges.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final boolean oldVal = k.getWatch().isNotifyNewChanges();
+ k.getWatch().setNotifyNewChanges(notifyNewChanges.getValue());
+ Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ k.getWatch().setNotifyNewChanges(oldVal);
+ notifyNewChanges.setValue(oldVal);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ notifyNewChanges.setValue(k.getWatch().isNotifyNewChanges());
+ table.setWidget(row, 3, notifyNewChanges);
+ }
+ {
+ final CheckBox notifyAllComments = new CheckBox();
+ notifyAllComments.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final boolean oldVal = k.getWatch().isNotifyAllComments();
+ k.getWatch().setNotifyAllComments(notifyAllComments.getValue());
+ Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ k.getWatch().setNotifyAllComments(oldVal);
+ notifyAllComments.setValue(oldVal);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ notifyAllComments.setValue(k.getWatch().isNotifyAllComments());
+ table.setWidget(row, 4, notifyAllComments);
+ }
+ {
+ final CheckBox notifySubmittedChanges = new CheckBox();
+ notifySubmittedChanges.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final boolean oldVal = k.getWatch().isNotifySubmittedChanges();
+ k.getWatch().setNotifySubmittedChanges(
+ notifySubmittedChanges.getValue());
+ Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ k.getWatch().setNotifySubmittedChanges(oldVal);
+ notifySubmittedChanges.setValue(oldVal);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ notifySubmittedChanges
+ .setValue(k.getWatch().isNotifySubmittedChanges());
+ table.setWidget(row, 5, notifySubmittedChanges);
+ }
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+ fmt.addStyleName(row, 3, S_DATA_CELL);
+ fmt.addStyleName(row, 4, S_DATA_CELL);
+ fmt.addStyleName(row, 5, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
new file mode 100644
index 0000000000..fd2a89d540
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
@@ -0,0 +1,105 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.InlineHyperlink;
+
+public class RegisterScreen extends AccountScreen {
+ private final String nextToken;
+
+ public RegisterScreen(final String next) {
+ nextToken = next;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ display();
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.welcomeToGerritCodeReview());
+
+ final FlowPanel formBody = new FlowPanel();
+
+ final FlowPanel contactGroup = new FlowPanel();
+ contactGroup.setStyleName("gerrit-RegisterScreen-Section");
+ contactGroup.add(new SmallHeading(Util.C.welcomeReviewContact()));
+ final HTML whereFrom = new HTML(Util.C.welcomeContactFrom());
+ whereFrom.setStyleName("gerrit-RegisterScreen-Explain");
+ contactGroup.add(whereFrom);
+ contactGroup.add(new ContactPanelShort() {
+ @Override
+ protected void display(final Account userAccount) {
+ super.display(userAccount);
+
+ if ("".equals(nameTxt.getText())) {
+ // No name? Encourage the user to provide us something.
+ //
+ nameTxt.setFocus(true);
+ save.setEnabled(true);
+ }
+ }
+ });
+ formBody.add(contactGroup);
+
+ final FlowPanel sshKeyGroup = new FlowPanel();
+ sshKeyGroup.setStyleName("gerrit-RegisterScreen-Section");
+ sshKeyGroup.add(new SmallHeading(Util.C.welcomeSshKeyHeading()));
+ final HTML whySshKey = new HTML(Util.C.welcomeSshKeyText());
+ whySshKey.setStyleName("gerrit-RegisterScreen-Explain");
+ sshKeyGroup.add(whySshKey);
+ sshKeyGroup.add(new SshPanel() {
+ {
+ setKeyTableVisible(false);
+ }
+ });
+ formBody.add(sshKeyGroup);
+
+ final FlowPanel choices = new FlowPanel();
+ choices.setStyleName("gerrit-RegisterScreen-NextLinks");
+ if (Gerrit.getConfig().isUseContributorAgreements()) {
+ final FlowPanel agreementGroup = new FlowPanel();
+ agreementGroup.setStyleName("gerrit-RegisterScreen-Section");
+ agreementGroup.add(new SmallHeading(Util.C.welcomeAgreementHeading()));
+ final HTML whyAgreement = new HTML(Util.C.welcomeAgreementText());
+ whyAgreement.setStyleName("gerrit-RegisterScreen-Explain");
+ agreementGroup.add(whyAgreement);
+
+ choices.add(new InlineHyperlink(Util.C.newAgreement(),
+ PageLinks.SETTINGS_NEW_AGREEMENT + "," + nextToken));
+ choices
+ .add(new InlineHyperlink(Util.C.welcomeAgreementLater(), nextToken));
+ formBody.add(agreementGroup);
+ } else {
+ choices.add(new InlineHyperlink(Util.C.welcomeContinue(), nextToken));
+ }
+ formBody.add(choices);
+
+ final FormPanel form = new FormPanel();
+ form.add(formBody);
+ add(form);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
new file mode 100644
index 0000000000..175cb05a52
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.data.SshHostKey;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
+
+class SshHostKeyPanel extends Composite {
+ SshHostKeyPanel(final SshHostKey info) {
+ final FlowPanel body = new FlowPanel();
+ body.setStyleName("gerrit-SshHostKeyPanel");
+ body.add(new SmallHeading(Util.C.sshHostKeyTitle()));
+ {
+ final Label fpLbl = new Label(Util.C.sshHostKeyFingerprint());
+ fpLbl.setStyleName("gerrit-SshHostKeyPanel-Heading");
+ body.add(fpLbl);
+ final Label fpVal = new Label(info.getFingerprint());
+ fpVal.setStyleName("gerrit-SshHostKeyPanel-FingerprintData");
+ body.add(fpVal);
+ }
+ {
+ final HTML hdr = new HTML(Util.C.sshHostKeyKnownHostEntry());
+ hdr.setStyleName("gerrit-SshHostKeyPanel-Heading");
+ body.add(hdr);
+ final CopyableLabel lbl =
+ new CopyableLabel(info.getHostIdent() + " " + info.getHostKey());
+ lbl.addStyleName("gerrit-SshHostKeyPanel-KnownHostEntry");
+ body.add(lbl);
+ }
+ initWidget(body);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java
new file mode 100644
index 0000000000..d49c61c4a1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/SshPanel.java
@@ -0,0 +1,588 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.ErrorDialog;
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.common.data.SshHostKey;
+import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.common.errors.InvalidSshUserNameException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtjsonrpc.client.RemoteJsonException;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.HashSet;
+import java.util.List;
+
+class SshPanel extends Composite {
+ private static boolean loadedApplet;
+ private static Element applet;
+ private static String appletErrorInvalidKey;
+ private static String appletErrorSecurity;
+
+ private int labelIdx, fieldIdx;
+
+ private NpTextBox userNameTxt;
+ private Button changeUserName;
+
+ private SshKeyTable keys;
+
+ private Button showAddKeyBlock;
+ private Panel addKeyBlock;
+ private Button closeAddKeyBlock;
+ private Button clearNew;
+ private Button addNew;
+ private Button browse;
+ private NpTextArea addTxt;
+ private Button delSel;
+
+ private Panel serverKeys;
+
+ SshPanel() {
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ labelIdx = 1;
+ fieldIdx = 0;
+ } else {
+ labelIdx = 0;
+ fieldIdx = 1;
+ }
+
+ final FlowPanel body = new FlowPanel();
+
+ userNameTxt = new NpTextBox();
+ if (Gerrit.isSignedIn()) {
+ userNameTxt.setText(Gerrit.getUserAccount().getSshUserName());
+ }
+ userNameTxt.addKeyPressHandler(new SshUserNameValidator());
+ userNameTxt.addStyleName("gerrit-SshPanel-username");
+ userNameTxt.setVisibleLength(16);
+ userNameTxt.setReadOnly(!canEditSshUserName());
+
+ changeUserName = new Button(Util.C.buttonChangeSshUserName());
+ changeUserName.setVisible(canEditSshUserName());
+ changeUserName.setEnabled(false);
+ changeUserName.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doChangeUserName();
+ }
+ });
+ new TextSaveButtonListener(userNameTxt, changeUserName);
+
+ final Grid userInfo = new Grid(1, 2);
+ userInfo.setStyleName("gerrit-InfoBlock");
+ userInfo.addStyleName("gerrit-AccountInfoBlock");
+ body.add(userInfo);
+
+ final FlowPanel userNameRow = new FlowPanel();
+ userNameRow.add(userNameTxt);
+ userNameRow.add(changeUserName);
+
+ row(userInfo, 0, Util.C.sshUserName(), userNameRow);
+ userInfo.getCellFormatter().addStyleName(0, 0, "topmost");
+ userInfo.getCellFormatter().addStyleName(0, 0, "topmost");
+ userInfo.getCellFormatter().addStyleName(0, 1, "topmost");
+ userInfo.getCellFormatter().addStyleName(0, 0, "bottomheader");
+
+ showAddKeyBlock = new Button(Util.C.buttonShowAddSshKey());
+ showAddKeyBlock.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ showAddKeyBlock(true);
+ }
+ });
+
+ keys = new SshKeyTable();
+ body.add(keys);
+ {
+ final FlowPanel fp = new FlowPanel();
+ delSel = new Button(Util.C.buttonDeleteSshKey());
+ delSel.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ keys.deleteChecked();
+ }
+ });
+ fp.add(delSel);
+ fp.add(showAddKeyBlock);
+ body.add(fp);
+ }
+
+ addKeyBlock = new VerticalPanel();
+ addKeyBlock.setVisible(false);
+ addKeyBlock.setStyleName("gerrit-AddSshKeyPanel");
+ addKeyBlock.add(new SmallHeading(Util.C.addSshKeyPanelHeader()));
+ addKeyBlock.add(new HTML(Util.C.addSshKeyHelp()));
+
+ addTxt = new NpTextArea();
+ addTxt.setVisibleLines(12);
+ addTxt.setCharacterWidth(80);
+ DOM.setElementPropertyBoolean(addTxt.getElement(), "spellcheck", false);
+ addKeyBlock.add(addTxt);
+
+ final HorizontalPanel buttons = new HorizontalPanel();
+ addKeyBlock.add(buttons);
+
+ clearNew = new Button(Util.C.buttonClearSshKeyInput());
+ clearNew.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ addTxt.setText("");
+ addTxt.setFocus(true);
+ }
+ });
+ buttons.add(clearNew);
+
+ browse = new Button(Util.C.buttonOpenSshKey());
+ browse.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doBrowse();
+ }
+ });
+ browse.setVisible(GWT.isScript() && (!loadedApplet || applet != null));
+ buttons.add(browse);
+
+ addNew = new Button(Util.C.buttonAddSshKey());
+ addNew.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddNew();
+ }
+ });
+ buttons.add(addNew);
+
+ closeAddKeyBlock = new Button(Util.C.buttonCloseAddSshKey());
+ closeAddKeyBlock.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ showAddKeyBlock(false);
+ }
+ });
+ buttons.add(closeAddKeyBlock);
+ buttons.setCellWidth(closeAddKeyBlock, "100%");
+ buttons.setCellHorizontalAlignment(closeAddKeyBlock,
+ HasHorizontalAlignment.ALIGN_RIGHT);
+
+ body.add(addKeyBlock);
+
+ serverKeys = new FlowPanel();
+ body.add(serverKeys);
+
+ initWidget(body);
+ }
+
+ private boolean canEditSshUserName() {
+ return Gerrit.getConfig().canEdit(Account.FieldName.SSH_USER_NAME);
+ }
+
+ protected void row(final Grid info, final int row, final String name,
+ final Widget field) {
+ info.setText(row, labelIdx, name);
+ info.setWidget(row, fieldIdx, field);
+ info.getCellFormatter().addStyleName(row, 0, "header");
+ }
+
+ void setKeyTableVisible(final boolean on) {
+ keys.setVisible(on);
+ delSel.setVisible(on);
+ closeAddKeyBlock.setVisible(on);
+ }
+
+ void doChangeUserName() {
+ if (!canEditSshUserName()) {
+ return;
+ }
+
+ String newName = userNameTxt.getText();
+ if ("".equals(newName)) {
+ newName = null;
+ }
+ if (newName != null && !newName.matches(Account.SSH_USER_NAME_PATTERN)) {
+ invalidUserName();
+ return;
+ }
+
+ userNameTxt.setEnabled(false);
+ changeUserName.setEnabled(false);
+
+ final String newSshUserName = newName;
+ Util.ACCOUNT_SEC.changeSshUserName(newSshUserName,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ userNameTxt.setEnabled(true);
+ changeUserName.setEnabled(false);
+ if (Gerrit.isSignedIn()) {
+ Gerrit.getUserAccount().setSshUserName(newSshUserName);
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ userNameTxt.setEnabled(true);
+ changeUserName.setEnabled(true);
+ if (InvalidSshUserNameException.MESSAGE.equals(caught.getMessage())) {
+ invalidUserName();
+ } else {
+ super.onFailure(caught);
+ }
+ }
+ });
+ }
+
+ void invalidUserName() {
+ userNameTxt.setFocus(true);
+ new ErrorDialog(Util.C.invalidSshUserName()).center();
+ }
+
+ void doBrowse() {
+ browse.setEnabled(false);
+ if (!loadedApplet) {
+ applet = DOM.createElement("applet");
+ applet.setAttribute("code",
+ "com.google.gerrit.keyapplet.ReadPublicKey.class");
+ applet.setAttribute("archive", GWT.getModuleBaseURL()
+ + "gerrit-keyapplet.cache.jar?v=" + Gerrit.getVersion());
+ applet.setAttribute("mayscript", "true");
+ applet.setAttribute("width", "0");
+ applet.setAttribute("height", "0");
+ RootPanel.getBodyElement().appendChild(applet);
+ loadedApplet = true;
+
+ // We have to defer to allow the event loop time to setup that
+ // new applet tag we just created above, and actually load the
+ // applet into the runtime.
+ //
+ DeferredCommand.addCommand(new Command() {
+ public void execute() {
+ doBrowse();
+ }
+ });
+ return;
+ }
+ if (applet == null) {
+ // If the applet element is null, the applet was determined
+ // to have failed to load, and we are dead. Hide the button.
+ //
+ noBrowse();
+ return;
+ }
+
+ String txt;
+ try {
+ txt = openPublicKey(applet);
+ } catch (RuntimeException re) {
+ // If this call fails, the applet is dead. It is most likely
+ // not loading due to Java support being disabled.
+ //
+ noBrowse();
+ return;
+ }
+ if (txt == null) {
+ txt = "";
+ }
+
+ browse.setEnabled(true);
+
+ if (appletErrorInvalidKey == null) {
+ appletErrorInvalidKey = getErrorInvalidKey(applet);
+ appletErrorSecurity = getErrorSecurity(applet);
+ }
+
+ if (appletErrorInvalidKey.equals(txt)) {
+ new ErrorDialog(Util.C.invalidSshKeyError()).center();
+ return;
+ }
+ if (appletErrorSecurity.equals(txt)) {
+ new ErrorDialog(Util.C.invalidSshKeyError()).center();
+ return;
+ }
+
+ addTxt.setText(txt);
+ addNew.setFocus(true);
+ }
+
+ private void noBrowse() {
+ if (applet != null) {
+ applet.getParentElement().removeChild(applet);
+ applet = null;
+ }
+ browse.setVisible(false);
+ new ErrorDialog(Util.C.sshJavaAppletNotAvailable()).center();
+ }
+
+ private static native String openPublicKey(Element keyapp)
+ /*-{ var r = keyapp.openPublicKey(); return r == null ? null : ''+r; }-*/;
+
+ private static native String getErrorInvalidKey(Element keyapp)
+ /*-{ return ''+keyapp.getErrorInvalidKey(); }-*/;
+
+ private static native String getErrorSecurity(Element keyapp)
+ /*-{ return ''+keyapp.getErrorSecurity(); }-*/;
+
+ void doAddNew() {
+ final String txt = addTxt.getText();
+ if (txt != null && txt.length() > 0) {
+ addNew.setEnabled(false);
+ Util.ACCOUNT_SEC.addSshKey(txt, new GerritCallback<AccountSshKey>() {
+ public void onSuccess(final AccountSshKey result) {
+ addNew.setEnabled(true);
+ addTxt.setText("");
+ keys.addOneKey(result);
+ if (!keys.isVisible()) {
+ showAddKeyBlock(false);
+ setKeyTableVisible(true);
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ addNew.setEnabled(true);
+
+ if (isInvalidSshKey(caught)) {
+ new ErrorDialog(Util.C.invalidSshKeyError()).center();
+
+ } else {
+ super.onFailure(caught);
+ }
+ }
+
+ private boolean isInvalidSshKey(final Throwable caught) {
+ if (caught instanceof InvalidSshKeyException) {
+ return true;
+ }
+ return caught instanceof RemoteJsonException
+ && InvalidSshKeyException.MESSAGE.equals(caught.getMessage());
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+
+ userNameTxt.setEnabled(false);
+ Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
+ public void onSuccess(final Account result) {
+ if (Gerrit.isSignedIn()) {
+ Gerrit.getUserAccount().setSshUserName(result.getSshUserName());
+ }
+ userNameTxt.setText(result.getSshUserName());
+ userNameTxt.setEnabled(true);
+ }
+ });
+
+ Util.ACCOUNT_SEC.mySshKeys(new GerritCallback<List<AccountSshKey>>() {
+ public void onSuccess(final List<AccountSshKey> result) {
+ keys.display(result);
+ if (result.isEmpty() && keys.isVisible()) {
+ showAddKeyBlock(true);
+ }
+ }
+ });
+
+ Gerrit.SYSTEM_SVC.daemonHostKeys(new GerritCallback<List<SshHostKey>>() {
+ public void onSuccess(final List<SshHostKey> result) {
+ serverKeys.clear();
+ for (final SshHostKey keyInfo : result) {
+ serverKeys.add(new SshHostKeyPanel(keyInfo));
+ }
+ }
+ });
+ }
+
+ private void showAddKeyBlock(final boolean show) {
+ showAddKeyBlock.setVisible(!show);
+ addKeyBlock.setVisible(show);
+ }
+
+ private final class SshUserNameValidator implements KeyPressHandler {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ final char code = event.getCharCode();
+ switch (code) {
+ case KeyCodes.KEY_ALT:
+ case KeyCodes.KEY_BACKSPACE:
+ case KeyCodes.KEY_CTRL:
+ case KeyCodes.KEY_DELETE:
+ case KeyCodes.KEY_DOWN:
+ case KeyCodes.KEY_END:
+ case KeyCodes.KEY_ENTER:
+ case KeyCodes.KEY_ESCAPE:
+ case KeyCodes.KEY_HOME:
+ case KeyCodes.KEY_LEFT:
+ case KeyCodes.KEY_PAGEDOWN:
+ case KeyCodes.KEY_PAGEUP:
+ case KeyCodes.KEY_RIGHT:
+ case KeyCodes.KEY_SHIFT:
+ case KeyCodes.KEY_TAB:
+ case KeyCodes.KEY_UP:
+ // Allow these, even if one of their assigned codes is
+ // identical to an ASCII character we do not want to
+ // allow in the box.
+ //
+ // We still want to let the user move around the input box
+ // with their arrow keys, or to move between fields using tab.
+ // Invalid characters introduced will be caught through the
+ // server's own validation of the input data.
+ //
+ break;
+
+ default:
+ final TextBox box = (TextBox) event.getSource();
+ final String re;
+ if (box.getCursorPos() == 0)
+ re = Account.SSH_USER_NAME_PATTERN_FIRST;
+ else
+ re = Account.SSH_USER_NAME_PATTERN_REST;
+ if (!String.valueOf(code).matches("^" + re + "$")) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+
+ private class SshKeyTable extends FancyFlexTable<AccountSshKey> {
+ private static final String S_INVALID = "gerrit-SshKeyPanel-Invalid";
+
+ SshKeyTable() {
+ table.setWidth("");
+ table.setText(0, 3, Util.C.sshKeyAlgorithm());
+ table.setText(0, 4, Util.C.sshKeyKey());
+ table.setText(0, 5, Util.C.sshKeyComment());
+ table.setText(0, 6, Util.C.sshKeyLastUsed());
+ table.setText(0, 7, Util.C.sshKeyStored());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ fmt.addStyleName(0, 4, S_DATA_HEADER);
+ fmt.addStyleName(0, 5, S_DATA_HEADER);
+ fmt.addStyleName(0, 6, S_DATA_HEADER);
+ fmt.addStyleName(0, 7, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<AccountSshKey.Id> ids = new HashSet<AccountSshKey.Id>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final AccountSshKey k = getRowItem(row);
+ if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
+ ids.add(k.getKey());
+ }
+ }
+ if (!ids.isEmpty()) {
+ Util.ACCOUNT_SEC.deleteSshKeys(ids, new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ for (int row = 1; row < table.getRowCount();) {
+ final AccountSshKey k = getRowItem(row);
+ if (k != null && ids.contains(k.getKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ if (table.getRowCount() == 1) {
+ showAddKeyBlock(true);
+ }
+ }
+ });
+ }
+ }
+
+ void display(final List<AccountSshKey> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountSshKey k : result) {
+ addOneKey(k);
+ }
+ }
+
+ void addOneKey(final AccountSshKey k) {
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+
+ table.setWidget(row, 1, new CheckBox());
+ if (k.isValid()) {
+ table.setText(row, 2, "");
+ fmt.removeStyleName(row, 2, S_INVALID);
+ } else {
+ table.setText(row, 2, Util.C.sshKeyInvalid());
+ fmt.addStyleName(row, 2, S_INVALID);
+ }
+ table.setText(row, 3, k.getAlgorithm());
+ table.setText(row, 4, elide(k.getEncodedKey()));
+ table.setText(row, 5, k.getComment());
+ table.setText(row, 6, FormatUtil.mediumFormat(k.getLastUsedOn()));
+ table.setText(row, 7, FormatUtil.mediumFormat(k.getStoredOn()));
+
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_ICON_CELL);
+ fmt.addStyleName(row, 4, "gerrit-SshKeyPanel-EncodedKey");
+ for (int c = 3; c <= 7; c++) {
+ fmt.addStyleName(row, c, S_DATA_CELL);
+ }
+ fmt.addStyleName(row, 6, "C_LAST_UPDATE");
+ fmt.addStyleName(row, 7, "C_LAST_UPDATE");
+
+ setRowItem(row, k);
+ }
+
+ String elide(final String s) {
+ if (s == null || s.length() < 40) {
+ return s;
+ }
+ return s.substring(0, 30) + "..." + s.substring(s.length() - 10);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java
new file mode 100644
index 0000000000..5ed79e6716
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/Util.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.common.data.AccountSecurity;
+import com.google.gerrit.common.data.AccountService;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class Util {
+ public static final AccountConstants C = GWT.create(AccountConstants.class);
+ public static final AccountMessages M = GWT.create(AccountMessages.class);
+ public static final AccountService ACCOUNT_SVC;
+ public static final AccountSecurity ACCOUNT_SEC;
+
+ static {
+ ACCOUNT_SVC = GWT.create(AccountService.class);
+ JsonUtil.bind(ACCOUNT_SVC, "rpc/AccountService");
+
+ ACCOUNT_SEC = GWT.create(AccountSecurity.class);
+ JsonUtil.bind(ACCOUNT_SEC, "rpc/AccountSecurity");
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
new file mode 100644
index 0000000000..db9d718894
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.account;
+
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gwt.user.client.History;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+public class ValidateEmailScreen extends AccountScreen {
+ private final String magicToken;
+
+ public ValidateEmailScreen(final String magicToken) {
+ this.magicToken = magicToken;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.accountSettingsHeading());
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.ACCOUNT_SEC.validateEmail(magicToken,
+ new ScreenLoadCallback<VoidResult>(this) {
+ @Override
+ protected void preDisplay(final VoidResult result) {
+ }
+
+ @Override
+ protected void postDisplay() {
+ History.newItem(PageLinks.SETTINGS_CONTACT, true);
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
new file mode 100644
index 0000000000..42fb296c32
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
@@ -0,0 +1,534 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountDashboardLink;
+import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.AddMemberBox;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.GroupDetail;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.HashSet;
+import java.util.List;
+
+public class AccountGroupScreen extends AccountScreen {
+ private final AccountGroup.Id groupId;
+ private AccountInfoCache accounts = AccountInfoCache.empty();
+ private MemberTable members;
+
+ private NpTextBox groupNameTxt;
+ private Button saveName;
+
+ private NpTextBox ownerTxtBox;
+ private SuggestBox ownerTxt;
+ private Button saveOwner;
+
+ private NpTextArea descTxt;
+ private Button saveDesc;
+
+ private Label typeSystem;
+ private ListBox typeSelect;
+ private Button saveType;
+
+ private Panel memberPanel;
+ private AddMemberBox addMemberBox;
+ private Button delMember;
+
+ private Panel externalPanel;
+ private Label externalName;
+ private NpTextBox externalNameFilter;
+ private Button externalNameSearch;
+ private Grid externalMatches;
+
+ public AccountGroupScreen(final AccountGroup.Id toShow) {
+ groupId = toShow;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>(
+ this) {
+ @Override
+ protected void preDisplay(final GroupDetail result) {
+ display(result);
+ }
+ });
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ initName();
+ initOwner();
+ initDescription();
+ initGroupType();
+ initMemberList();
+ initExternal();
+ }
+
+ private void initName() {
+ final VerticalPanel groupNamePanel = new VerticalPanel();
+ groupNameTxt = new NpTextBox();
+ groupNameTxt.setVisibleLength(60);
+ groupNamePanel.add(groupNameTxt);
+
+ saveName = new Button(Util.C.buttonRenameGroup());
+ saveName.setEnabled(false);
+ saveName.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final String newName = groupNameTxt.getText().trim();
+ Util.GROUP_SVC.renameGroup(groupId, newName,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ saveName.setEnabled(false);
+ setPageTitle(Util.M.group(newName));
+ }
+ });
+ }
+ });
+ groupNamePanel.add(saveName);
+ add(groupNamePanel);
+
+ new TextSaveButtonListener(groupNameTxt, saveName);
+ }
+
+ private void initOwner() {
+ final VerticalPanel ownerPanel = new VerticalPanel();
+ ownerPanel.add(new SmallHeading(Util.C.headingOwner()));
+
+ ownerTxtBox = new NpTextBox();
+ ownerTxtBox.setVisibleLength(60);
+ ownerTxt = new SuggestBox(new AccountGroupSuggestOracle(), ownerTxtBox);
+ ownerPanel.add(ownerTxt);
+
+ saveOwner = new Button(Util.C.buttonChangeGroupOwner());
+ saveOwner.setEnabled(false);
+ saveOwner.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final String newOwner = ownerTxt.getText().trim();
+ if (newOwner.length() > 0) {
+ Util.GROUP_SVC.changeGroupOwner(groupId, newOwner,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ saveOwner.setEnabled(false);
+ }
+ });
+ }
+ }
+ });
+ ownerPanel.add(saveOwner);
+ add(ownerPanel);
+
+ new TextSaveButtonListener(ownerTxtBox, saveOwner);
+ }
+
+ private void initDescription() {
+ final VerticalPanel vp = new VerticalPanel();
+ vp.add(new SmallHeading(Util.C.headingDescription()));
+
+ descTxt = new NpTextArea();
+ descTxt.setVisibleLines(6);
+ descTxt.setCharacterWidth(60);
+ vp.add(descTxt);
+
+ saveDesc = new Button(Util.C.buttonSaveDescription());
+ saveDesc.setEnabled(false);
+ saveDesc.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final String txt = descTxt.getText().trim();
+ Util.GROUP_SVC.changeGroupDescription(groupId, txt,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ saveDesc.setEnabled(false);
+ }
+ });
+ }
+ });
+ vp.add(saveDesc);
+ add(vp);
+
+ new TextSaveButtonListener(descTxt, saveDesc);
+ }
+
+ private void initGroupType() {
+ typeSystem = new Label(Util.C.groupType_SYSTEM());
+
+ typeSelect = new ListBox();
+ typeSelect.addItem(Util.C.groupType_INTERNAL(), AccountGroup.Type.INTERNAL.name());
+ typeSelect.addItem(Util.C.groupType_LDAP(), AccountGroup.Type.LDAP.name());
+ typeSelect.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(ChangeEvent event) {
+ saveType.setEnabled(true);
+ }
+ });
+
+ saveType = new Button(Util.C.buttonChangeGroupType());
+ saveType.setEnabled(false);
+ saveType.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ onSaveType();
+ }
+ });
+
+ final VerticalPanel fp = new VerticalPanel();
+ fp.add(new SmallHeading(Util.C.headingGroupType()));
+ fp.add(typeSystem);
+ fp.add(typeSelect);
+ fp.add(saveType);
+ add(fp);
+ }
+
+ private void initMemberList() {
+ addMemberBox = new AddMemberBox();
+
+ addMemberBox.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddNew();
+ }
+ });
+
+ members = new MemberTable();
+
+ delMember = new Button(Util.C.buttonDeleteGroupMembers());
+ delMember.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ members.deleteChecked();
+ }
+ });
+
+ memberPanel = new FlowPanel();
+ memberPanel.add(new SmallHeading(Util.C.headingMembers()));
+ memberPanel.add(addMemberBox);
+ memberPanel.add(members);
+ memberPanel.add(delMember);
+ add(memberPanel);
+ }
+
+ private void initExternal() {
+ externalName = new Label();
+
+ externalNameFilter = new NpTextBox();
+ externalNameFilter.setVisibleLength(30);
+ externalNameFilter.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+ doExternalSearch();
+ }
+ }
+ });
+
+ externalNameSearch = new Button(Gerrit.C.searchButton());
+ externalNameSearch.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ doExternalSearch();
+ }
+ });
+
+ externalMatches = new Grid();
+ externalMatches.setStyleName("gerrit-InfoTable");
+ externalMatches.setVisible(false);
+
+ final FlowPanel searchLine = new FlowPanel();
+ searchLine.add(externalNameFilter);
+ searchLine.add(externalNameSearch);
+
+ externalPanel = new VerticalPanel();
+ externalPanel.add(new SmallHeading(Util.C.headingExternalGroup()));
+ externalPanel.add(externalName);
+ externalPanel.add(searchLine);
+ externalPanel.add(externalMatches);
+ add(externalPanel);
+ }
+
+ private void setType(final AccountGroup.Type newType) {
+ final boolean system = newType == AccountGroup.Type.SYSTEM;
+
+ typeSystem.setVisible(system);
+ typeSelect.setVisible(!system);
+ saveType.setVisible(!system);
+ memberPanel.setVisible(newType == AccountGroup.Type.INTERNAL);
+ externalPanel.setVisible(newType == AccountGroup.Type.LDAP);
+ externalNameFilter.setText(groupNameTxt.getText());
+
+ if (!system) {
+ for (int i = 0; i < typeSelect.getItemCount(); i++) {
+ if (newType.name().equals(typeSelect.getValue(i))) {
+ typeSelect.setSelectedIndex(i);
+ break;
+ }
+ }
+ }
+
+ saveType.setEnabled(false);
+ }
+
+ private void onSaveType() {
+ final int idx = typeSelect.getSelectedIndex();
+ final AccountGroup.Type newType =
+ AccountGroup.Type.valueOf(typeSelect.getValue(idx));
+
+ typeSelect.setEnabled(false);
+ saveType.setEnabled(false);
+
+ Util.GROUP_SVC.changeGroupType(groupId, newType,
+ new GerritCallback<VoidResult>() {
+ @Override
+ public void onSuccess(VoidResult result) {
+ typeSelect.setEnabled(true);
+ setType(newType);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ typeSelect.setEnabled(true);
+ saveType.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void doExternalSearch() {
+ externalNameFilter.setEnabled(false);
+ externalNameSearch.setEnabled(false);
+ Util.GROUP_SVC.searchExternalGroups(externalNameFilter.getText(),
+ new GerritCallback<List<AccountGroup.ExternalNameKey>>() {
+ @Override
+ public void onSuccess(List<AccountGroup.ExternalNameKey> result) {
+ final CellFormatter fmt = externalMatches.getCellFormatter();
+
+ if (result.isEmpty()) {
+ externalMatches.resize(1, 1);
+ externalMatches.setText(0, 0, Util.C.errorNoMatchingGroups());
+ fmt.setStyleName(0, 0, "header");
+ return;
+ }
+
+ externalMatches.resize(1 + result.size(), 2);
+
+ externalMatches.setText(0, 0, Util.C.columnGroupName());
+ externalMatches.setText(0, 1, "");
+ fmt.setStyleName(0, 0, "header");
+ fmt.setStyleName(0, 1, "header");
+
+ for (int row = 0; row < result.size(); row++) {
+ final AccountGroup.ExternalNameKey key = result.get(row);
+ final Button b = new Button(Util.C.buttonSelectGroup());
+ b.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ setExternalGroup(key);
+ }
+ });
+ externalMatches.setText(1 + row, 0, key.get());
+ externalMatches.setWidget(1 + row, 1, b);
+ fmt.setStyleName(1 + row, 1, "rightmost");
+ }
+ externalMatches.setVisible(true);
+
+ externalNameFilter.setEnabled(true);
+ externalNameSearch.setEnabled(true);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ externalNameFilter.setEnabled(true);
+ externalNameSearch.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void setExternalGroup(final AccountGroup.ExternalNameKey key) {
+ externalMatches.setVisible(false);
+
+ Util.GROUP_SVC.changeExternalGroup(groupId, key,
+ new GerritCallback<VoidResult>() {
+ @Override
+ public void onSuccess(VoidResult result) {
+ externalName.setText(key.get());
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ externalMatches.setVisible(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void display(final GroupDetail result) {
+ final AccountGroup group = result.group;
+ setPageTitle(Util.M.group(group.getName()));
+ groupNameTxt.setText(group.getName());
+ if (result.ownerGroup != null) {
+ ownerTxt.setText(result.ownerGroup.getName());
+ } else {
+ ownerTxt.setText(Util.M.deletedGroup(group.getOwnerGroupId().get()));
+ }
+ descTxt.setText(group.getDescription());
+
+ switch (group.getType()) {
+ case INTERNAL:
+ accounts = result.accounts;
+ members.display(result.members);
+ break;
+
+ case LDAP:
+ externalName.setText(group.getExternalNameKey() != null ? group
+ .getExternalNameKey().get() : Util.C.noGroupSelected());
+ break;
+ }
+
+ setType(group.getType());
+ }
+
+ void doAddNew() {
+ final String nameEmail = addMemberBox.getText();
+ if (nameEmail.length() == 0) {
+ return;
+ }
+
+ addMemberBox.setEnabled(false);
+ Util.GROUP_SVC.addGroupMember(groupId, nameEmail,
+ new GerritCallback<GroupDetail>() {
+ public void onSuccess(final GroupDetail result) {
+ addMemberBox.setEnabled(true);
+ addMemberBox.setText("");
+ if (result.accounts != null && result.members != null) {
+ accounts.merge(result.accounts);
+ members.display(result.members);
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ addMemberBox.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private class MemberTable extends FancyFlexTable<AccountGroupMember> {
+ MemberTable() {
+ table.setText(0, 2, Util.C.columnMember());
+ table.setText(0, 3, Util.C.columnEmailAddress());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<AccountGroupMember.Key> ids =
+ new HashSet<AccountGroupMember.Key>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final AccountGroupMember k = getRowItem(row);
+ if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
+ ids.add(k.getKey());
+ }
+ }
+ if (!ids.isEmpty()) {
+ Util.GROUP_SVC.deleteGroupMembers(groupId, ids,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ for (int row = 1; row < table.getRowCount();) {
+ final AccountGroupMember k = getRowItem(row);
+ if (k != null && ids.contains(k.getKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ }
+ });
+ }
+ }
+
+ void insertMember(final AccountGroupMember k) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+
+ void display(final List<AccountGroupMember> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountGroupMember k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final AccountGroupMember k) {
+ final Account.Id accountId = k.getAccountId();
+ table.setWidget(row, 1, new CheckBox());
+ table.setWidget(row, 2, AccountDashboardLink.link(accounts, accountId));
+ table.setText(row, 3, accounts.get(accountId).getPreferredEmail());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+ fmt.addStyleName(row, 3, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index bdbbd41714..bdbbd41714 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
diff --git a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index 4b24fe6a1c..4b24fe6a1c 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/admin/AdminMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
index ab3541e13b..ab3541e13b 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
diff --git a/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
index 6feb69aeb8..6feb69aeb8 100644
--- a/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
new file mode 100644
index 0000000000..b00491fb02
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+import java.util.List;
+
+public class GroupListScreen extends AccountScreen {
+ private GroupTable groups;
+
+ private NpTextBox addTxt;
+ private Button addNew;
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.GROUP_SVC
+ .ownedGroups(new ScreenLoadCallback<List<AccountGroup>>(this) {
+ @Override
+ protected void preDisplay(final List<AccountGroup> result) {
+ groups.display(result);
+ groups.finishDisplay();
+ }
+ });
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.groupListTitle());
+
+ groups = new GroupTable(true /* hyperlink to admin */, PageLinks.ADMIN_GROUPS);
+ add(groups);
+
+ final VerticalPanel fp = new VerticalPanel();
+ fp.setStyleName("gerrit-AddSshKeyPanel");
+ fp.add(new SmallHeading(Util.C.headingCreateGroup()));
+
+ addTxt = new NpTextBox();
+ addTxt.setVisibleLength(60);
+ fp.add(addTxt);
+
+ addNew = new Button(Util.C.buttonCreateGroup());
+ addNew.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doCreateGroup();
+ }
+ });
+ fp.add(addNew);
+ add(fp);
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ groups.setRegisterKeys(true);
+ }
+
+ private void doCreateGroup() {
+ final String newName = addTxt.getText();
+ if (newName == null || newName.length() == 0) {
+ return;
+ }
+
+ Util.GROUP_SVC.createGroup(newName, new GerritCallback<AccountGroup.Id>() {
+ public void onSuccess(final AccountGroup.Id result) {
+ History.newItem(HistoryHandler.toAccountGroup(result));
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
new file mode 100644
index 0000000000..24e31c0af8
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupTable.java
@@ -0,0 +1,104 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.Hyperlink;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+
+import java.util.List;
+
+
+public class GroupTable extends NavigationTable<AccountGroup> {
+ private final boolean enableLink;
+
+ public GroupTable(final boolean enableLink) {
+ this(enableLink, null);
+ }
+
+ public GroupTable(final boolean enableLink, final String pointerId) {
+ this.enableLink = enableLink;
+
+ setSavePointerId(pointerId);
+ keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.groupListPrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.groupListNext()));
+ keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.groupListOpen()));
+ keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
+ .groupListOpen()));
+
+ table.setText(0, 1, Util.C.columnGroupName());
+ table.setText(0, 2, Util.C.columnGroupDescription());
+ table.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ final Cell cell = table.getCellForEvent(event);
+ if (cell != null && cell.getCellIndex() != 1
+ && getRowItem(cell.getRowIndex()) != null) {
+ movePointerTo(cell.getRowIndex());
+ }
+ }
+ });
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_DATA_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ }
+
+ @Override
+ protected Object getRowItemKey(final AccountGroup item) {
+ return item.getId();
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ History.newItem(HistoryHandler.toAccountGroup(getRowItem(row).getId()));
+ }
+
+ public void display(final List<AccountGroup> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final AccountGroup k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final AccountGroup k) {
+ if (enableLink) {
+ table.setWidget(row, 1, new Hyperlink(k.getName(), HistoryHandler.toAccountGroup(k
+ .getId())));
+ } else {
+ table.setText(row, 1, k.getName());
+ }
+ table.setText(row, 2, k.getDescription());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_DATA_CELL);
+ fmt.addStyleName(row, 1, "GroupName");
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java
new file mode 100644
index 0000000000..ed4acd682f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.LazyPanel;
+import com.google.gwt.user.client.ui.TabPanel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProjectAdminScreen extends AccountScreen {
+ static final String INFO_TAB = "info";
+ static final String BRANCH_TAB = "branches";
+ static final String ACCESS_TAB = "access";
+
+ private final Project.NameKey projectName;
+ private final String initialTabToken;
+
+ private List<String> tabTokens;
+ private TabPanel tabs;
+
+ public ProjectAdminScreen(final Project.NameKey toShow, final String token) {
+ projectName = toShow;
+ initialTabToken = token;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.PROJECT_SVC.projectDetail(projectName,
+ new ScreenLoadCallback<ProjectDetail>(this) {
+ @Override
+ protected void preDisplay(final ProjectDetail result) {
+ display(result);
+ tabs.selectTab(tabTokens.indexOf(initialTabToken));
+ }
+ });
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ tabTokens = new ArrayList<String>();
+ tabs = new TabPanel();
+ tabs.setWidth("98%");
+ add(tabs);
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ProjectInfoPanel createWidget() {
+ return new ProjectInfoPanel(projectName);
+ }
+ }, Util.C.projectAdminTabGeneral());
+ tabTokens.add(HistoryHandler.toProjectAdmin(projectName, INFO_TAB));
+
+ if (!Gerrit.getConfig().getWildProject().equals(projectName)) {
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ProjectBranchesPanel createWidget() {
+ return new ProjectBranchesPanel(projectName);
+ }
+ }, Util.C.projectAdminTabBranches());
+ tabTokens.add(HistoryHandler.toProjectAdmin(projectName, BRANCH_TAB));
+ }
+
+ tabs.add(new LazyPanel() {
+ @Override
+ protected ProjectRightsPanel createWidget() {
+ return new ProjectRightsPanel(projectName);
+ }
+ }, Util.C.projectAdminTabAccess());
+ tabTokens.add(HistoryHandler.toProjectAdmin(projectName, ACCESS_TAB));
+
+ tabs.addSelectionHandler(new SelectionHandler<Integer>() {
+ @Override
+ public void onSelection(final SelectionEvent<Integer> event) {
+ Gerrit.display(tabTokens.get(event.getSelectedItem()), false);
+ }
+ });
+ }
+
+
+ private void display(final ProjectDetail result) {
+ final Project project = result.project;
+ setPageTitle(Util.M.project(project.getName()));
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java
new file mode 100644
index 0000000000..080ba8c380
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java
@@ -0,0 +1,288 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.common.data.GitwebLink;
+import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.common.errors.InvalidRevisionException;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtjsonrpc.client.RemoteJsonException;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ProjectBranchesPanel extends Composite {
+ private Project.NameKey projectName;
+
+ private BranchesTable branches;
+ private Button delBranch;
+ private Button addBranch;
+ private NpTextBox nameTxtBox;
+ private NpTextBox irevTxtBox;
+
+ public ProjectBranchesPanel(final Project.NameKey toShow) {
+ final FlowPanel body = new FlowPanel();
+ initBranches(body);
+ initWidget(body);
+
+ projectName = toShow;
+ }
+
+ @Override
+ protected void onLoad() {
+ enableForm(false);
+ super.onLoad();
+
+ Util.PROJECT_SVC.listBranches(projectName,
+ new GerritCallback<List<Branch>>() {
+ public void onSuccess(final List<Branch> result) {
+ enableForm(true);
+ branches.display(result);
+ }
+ });
+ }
+
+ private void enableForm(final boolean on) {
+ delBranch.setEnabled(on);
+ addBranch.setEnabled(on);
+ nameTxtBox.setEnabled(on);
+ irevTxtBox.setEnabled(on);
+ }
+
+ private void initBranches(final Panel body) {
+ final FlowPanel addPanel = new FlowPanel();
+ addPanel.setStyleName("gerrit-AddSshKeyPanel");
+
+ final Grid addGrid = new Grid(2, 2);
+
+ nameTxtBox = new NpTextBox();
+ nameTxtBox.setVisibleLength(50);
+ nameTxtBox.setText(Util.C.defaultBranchName());
+ nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ nameTxtBox.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (Util.C.defaultBranchName().equals(nameTxtBox.getText())) {
+ nameTxtBox.setText("");
+ nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ nameTxtBox.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if ("".equals(nameTxtBox.getText())) {
+ nameTxtBox.setText(Util.C.defaultBranchName());
+ nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ addGrid.setText(0, 0, Util.C.columnBranchName() + ":");
+ addGrid.setWidget(0, 1, nameTxtBox);
+
+ irevTxtBox = new NpTextBox();
+ irevTxtBox.setVisibleLength(50);
+ irevTxtBox.setText(Util.C.defaultRevisionSpec());
+ irevTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ irevTxtBox.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (Util.C.defaultRevisionSpec().equals(irevTxtBox.getText())) {
+ irevTxtBox.setText("");
+ irevTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ irevTxtBox.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if ("".equals(irevTxtBox.getText())) {
+ irevTxtBox.setText(Util.C.defaultRevisionSpec());
+ irevTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ addGrid.setText(1, 0, Util.C.initialRevision() + ":");
+ addGrid.setWidget(1, 1, irevTxtBox);
+
+ addBranch = new Button(Util.C.buttonAddBranch());
+ addBranch.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddNewBranch();
+ }
+ });
+ addPanel.add(addGrid);
+ addPanel.add(addBranch);
+
+ branches = new BranchesTable();
+
+ delBranch = new Button(Util.C.buttonDeleteBranch());
+ delBranch.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ branches.deleteChecked();
+ }
+ });
+
+ body.add(branches);
+ body.add(delBranch);
+ body.add(addPanel);
+ }
+
+ private void doAddNewBranch() {
+ String branchName = nameTxtBox.getText();
+ if ("".equals(branchName) || Util.C.defaultBranchName().equals(branchName)) {
+ return;
+ }
+
+ String rev = irevTxtBox.getText();
+ if ("".equals(rev) || Util.C.defaultRevisionSpec().equals(rev)) {
+ return;
+ }
+
+ if (!branchName.startsWith(Branch.R_REFS)) {
+ branchName = Branch.R_HEADS + branchName;
+ }
+
+ addBranch.setEnabled(false);
+ Util.PROJECT_SVC.addBranch(projectName, branchName, rev,
+ new GerritCallback<List<Branch>>() {
+ public void onSuccess(final List<Branch> result) {
+ addBranch.setEnabled(true);
+ nameTxtBox.setText("");
+ irevTxtBox.setText("");
+ branches.display(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ if (caught instanceof InvalidNameException
+ || caught instanceof RemoteJsonException
+ && caught.getMessage().equals(InvalidNameException.MESSAGE)) {
+ nameTxtBox.selectAll();
+ nameTxtBox.setFocus(true);
+
+ } else if (caught instanceof InvalidRevisionException
+ || caught instanceof RemoteJsonException
+ && caught.getMessage().equals(InvalidRevisionException.MESSAGE)) {
+ irevTxtBox.selectAll();
+ irevTxtBox.setFocus(true);
+ }
+
+ addBranch.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private class BranchesTable extends FancyFlexTable<Branch> {
+ BranchesTable() {
+ table.setWidth("");
+ table.setText(0, 2, Util.C.columnBranchName());
+ table.setText(0, 3, Util.C.columnBranchRevision());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ fmt.addStyleName(0, 4, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<Branch.NameKey> ids = new HashSet<Branch.NameKey>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final Branch k = getRowItem(row);
+ if (k != null && table.getWidget(row, 1) instanceof CheckBox
+ && ((CheckBox) table.getWidget(row, 1)).getValue()) {
+ ids.add(k.getNameKey());
+ }
+ }
+ if (ids.isEmpty()) {
+ return;
+ }
+
+ Util.PROJECT_SVC.deleteBranch(projectName, ids,
+ new GerritCallback<Set<Branch.NameKey>>() {
+ public void onSuccess(final Set<Branch.NameKey> deleted) {
+ for (int row = 1; row < table.getRowCount();) {
+ final Branch k = getRowItem(row);
+ if (k != null && deleted.contains(k.getNameKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ }
+ });
+ }
+
+ void display(final List<Branch> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final Branch k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final Branch k) {
+ final GitwebLink c = Gerrit.getConfig().getGitwebLink();
+
+ table.setWidget(row, 1, new CheckBox());
+ table.setText(row, 2, k.getShortName());
+
+ if (k.getRevision() != null) {
+ table.setText(row, 3, k.getRevision().get());
+ }
+
+ if (c != null) {
+ table.setWidget(row, 4, new Anchor("(gitweb)", false, c.toBranch(k
+ .getNameKey())));
+ }
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+ fmt.addStyleName(row, 3, S_DATA_CELL);
+ fmt.addStyleName(row, 4, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
new file mode 100644
index 0000000000..ea5fba64f4
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
@@ -0,0 +1,205 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.client.ui.TextSaveButtonListener;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+
+public class ProjectInfoPanel extends Composite {
+ private Project.NameKey projectName;
+ private Project project;
+
+ private Panel submitTypePanel;
+ private ListBox submitType;
+
+ private Panel agreementsPanel;
+ private CheckBox useContributorAgreements;
+ private CheckBox useSignedOffBy;
+
+ private NpTextArea descTxt;
+ private Button saveProject;
+
+ public ProjectInfoPanel(final Project.NameKey toShow) {
+ saveProject = new Button(Util.C.buttonSaveChanges());
+ saveProject.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ doSave();
+ }
+ });
+
+ final FlowPanel body = new FlowPanel();
+ initDescription(body);
+ initSubmitType(body);
+ initAgreements(body);
+ body.add(saveProject);
+
+ initWidget(body);
+ projectName = toShow;
+ }
+
+ @Override
+ protected void onLoad() {
+ enableForm(false);
+ saveProject.setEnabled(false);
+ super.onLoad();
+ refresh();
+ }
+
+ private void refresh() {
+ Util.PROJECT_SVC.projectDetail(projectName,
+ new GerritCallback<ProjectDetail>() {
+ public void onSuccess(final ProjectDetail result) {
+ enableForm(true);
+ saveProject.setEnabled(false);
+ display(result);
+ }
+ });
+ }
+
+ private void enableForm(final boolean on) {
+ submitType.setEnabled(on);
+ descTxt.setEnabled(on);
+ useContributorAgreements.setEnabled(on);
+ useSignedOffBy.setEnabled(on);
+ }
+
+ private void initDescription(final Panel body) {
+ final VerticalPanel vp = new VerticalPanel();
+ vp.add(new SmallHeading(Util.C.headingDescription()));
+
+ descTxt = new NpTextArea();
+ descTxt.setVisibleLines(6);
+ descTxt.setCharacterWidth(60);
+ vp.add(descTxt);
+
+ body.add(vp);
+ new TextSaveButtonListener(descTxt, saveProject);
+ }
+
+ private void initSubmitType(final Panel body) {
+ submitTypePanel = new VerticalPanel();
+ submitTypePanel.add(new SmallHeading(Util.C.headingSubmitType()));
+
+ submitType = new ListBox();
+ for (final Project.SubmitType type : Project.SubmitType.values()) {
+ submitType.addItem(Util.toLongString(type), type.name());
+ }
+ submitType.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(final ChangeEvent event) {
+ saveProject.setEnabled(true);
+ }
+ });
+ submitTypePanel.add(submitType);
+ body.add(submitTypePanel);
+ }
+
+ private void initAgreements(final Panel body) {
+ final ValueChangeHandler<Boolean> onChangeSave =
+ new ValueChangeHandler<Boolean>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<Boolean> event) {
+ saveProject.setEnabled(true);
+ }
+ };
+
+ agreementsPanel = new VerticalPanel();
+ agreementsPanel.add(new SmallHeading(Util.C.headingAgreements()));
+
+ useContributorAgreements = new CheckBox(Util.C.useContributorAgreements());
+ useContributorAgreements.addValueChangeHandler(onChangeSave);
+ agreementsPanel.add(useContributorAgreements);
+
+ useSignedOffBy = new CheckBox(Util.C.useSignedOffBy(), true);
+ useSignedOffBy.addValueChangeHandler(onChangeSave);
+ agreementsPanel.add(useSignedOffBy);
+
+ body.add(agreementsPanel);
+ }
+
+ private void setSubmitType(final Project.SubmitType newSubmitType) {
+ if (submitType != null) {
+ for (int i = 0; i < submitType.getItemCount(); i++) {
+ if (newSubmitType.name().equals(submitType.getValue(i))) {
+ submitType.setSelectedIndex(i);
+ return;
+ }
+ }
+ submitType.setSelectedIndex(-1);
+ }
+ }
+
+ void display(final ProjectDetail result) {
+ project = result.project;
+
+ final boolean isall =
+ Gerrit.getConfig().getWildProject().equals(project.getNameKey());
+ submitTypePanel.setVisible(!isall);
+ agreementsPanel.setVisible(!isall);
+ useContributorAgreements.setVisible(Gerrit.getConfig()
+ .isUseContributorAgreements());
+
+ descTxt.setText(project.getDescription());
+ useContributorAgreements.setValue(project.isUseContributorAgreements());
+ useSignedOffBy.setValue(project.isUseSignedOffBy());
+ setSubmitType(project.getSubmitType());
+ }
+
+ private void doSave() {
+ project.setDescription(descTxt.getText().trim());
+ project.setUseContributorAgreements(useContributorAgreements.getValue());
+ project.setUseSignedOffBy(useSignedOffBy.getValue());
+ if (submitType.getSelectedIndex() >= 0) {
+ project.setSubmitType(Project.SubmitType.valueOf(submitType
+ .getValue(submitType.getSelectedIndex())));
+ }
+
+ enableForm(false);
+ saveProject.setEnabled(false);
+
+ Util.PROJECT_SVC.changeProjectSettings(project,
+ new GerritCallback<ProjectDetail>() {
+ public void onSuccess(final ProjectDetail result) {
+ enableForm(true);
+ display(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ refresh();
+ super.onFailure(caught);
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
new file mode 100644
index 0000000000..624efbd8f5
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -0,0 +1,134 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.Hyperlink;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+
+import java.util.List;
+
+public class ProjectListScreen extends AccountScreen {
+ private ProjectTable projects;
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.PROJECT_SVC.ownedProjects(new ScreenLoadCallback<List<Project>>(this) {
+ @Override
+ protected void preDisplay(final List<Project> result) {
+ projects.display(result);
+ projects.finishDisplay();
+ }
+ });
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.C.projectListTitle());
+
+ projects = new ProjectTable();
+ add(projects);
+
+ final VerticalPanel fp = new VerticalPanel();
+ fp.setStyleName("gerrit-AddSshKeyPanel");
+ fp.add(new SmallHeading(Util.C.headingCreateGroup()));
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ projects.setRegisterKeys(true);
+ }
+
+ private class ProjectTable extends NavigationTable<Project> {
+ ProjectTable() {
+ setSavePointerId(PageLinks.ADMIN_PROJECTS);
+ keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.projectListPrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.projectListNext()));
+ keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.projectListOpen()));
+ keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
+ .projectListOpen()));
+
+ table.setText(0, 1, Util.C.columnProjectName());
+ table.setText(0, 2, Util.C.columnProjectDescription());
+ table.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Cell cell = table.getCellForEvent(event);
+ if (cell != null && cell.getCellIndex() != 1
+ && getRowItem(cell.getRowIndex()) != null) {
+ movePointerTo(cell.getRowIndex());
+ }
+ }
+ });
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_DATA_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ }
+
+ @Override
+ protected Object getRowItemKey(final Project item) {
+ return item.getId();
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ History.newItem(link(getRowItem(row)));
+ }
+
+ private String link(final Project item) {
+ return HistoryHandler.toProjectAdmin(item.getNameKey(), ProjectAdminScreen.INFO_TAB);
+ }
+
+ void display(final List<Project> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final Project k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, k);
+ }
+ }
+
+ void populate(final int row, final Project k) {
+ table.setWidget(row, 1, new Hyperlink(k.getName(), link(k)));
+ table.setText(row, 2, k.getDescription());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_DATA_CELL);
+ fmt.addStyleName(row, 1, "C_PROJECT");
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+
+ setRowItem(row, k);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
new file mode 100644
index 0000000000..510d478712
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
@@ -0,0 +1,438 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+public class ProjectRightsPanel extends Composite {
+ private Project.NameKey projectName;
+
+ private RightsTable rights;
+ private Button delRight;
+ private Button addRight;
+ private ListBox catBox;
+ private ListBox rangeMinBox;
+ private ListBox rangeMaxBox;
+ private NpTextBox nameTxtBox;
+ private SuggestBox nameTxt;
+
+ public ProjectRightsPanel(final Project.NameKey toShow) {
+ projectName = toShow;
+
+ final FlowPanel body = new FlowPanel();
+ initRights(body);
+ initWidget(body);
+ }
+
+ @Override
+ protected void onLoad() {
+ enableForm(false);
+ super.onLoad();
+
+ Util.PROJECT_SVC.projectDetail(projectName,
+ new GerritCallback<ProjectDetail>() {
+ public void onSuccess(final ProjectDetail result) {
+ enableForm(true);
+ display(result);
+ }
+ });
+ }
+
+ private void enableForm(final boolean on) {
+ delRight.setEnabled(on);
+
+ final boolean canAdd = on && catBox.getItemCount() > 0;
+ addRight.setEnabled(canAdd);
+ nameTxtBox.setEnabled(canAdd);
+ catBox.setEnabled(canAdd);
+ rangeMinBox.setEnabled(canAdd);
+ rangeMaxBox.setEnabled(canAdd);
+ }
+
+ private void initRights(final Panel body) {
+ final FlowPanel addPanel = new FlowPanel();
+ addPanel.setStyleName("gerrit-AddSshKeyPanel");
+
+ final Grid addGrid = new Grid(4, 2);
+
+ catBox = new ListBox();
+ rangeMinBox = new ListBox();
+ rangeMaxBox = new ListBox();
+
+ catBox.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(final ChangeEvent event) {
+ populateRangeBoxes();
+ }
+ });
+ for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
+ .getApprovalTypes()) {
+ final ApprovalCategory c = at.getCategory();
+ catBox.addItem(c.getName(), c.getId().get());
+ }
+ for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
+ .getActionTypes()) {
+ final ApprovalCategory c = at.getCategory();
+ if (Gerrit.getConfig().getWildProject().equals(projectName)
+ && ApprovalCategory.OWN.equals(c.getId())) {
+ // Giving out control of the WILD_PROJECT to other groups beyond
+ // Administrators is dangerous. Having control over WILD_PROJECT
+ // is about the same as having Administrator access as users are
+ // able to affect grants in all projects on the system.
+ //
+ continue;
+ }
+ catBox.addItem(c.getName(), c.getId().get());
+ }
+ if (catBox.getItemCount() > 0) {
+ catBox.setSelectedIndex(0);
+ populateRangeBoxes();
+ }
+
+ addGrid.setText(0, 0, Util.C.columnApprovalCategory() + ":");
+ addGrid.setWidget(0, 1, catBox);
+
+ nameTxtBox = new NpTextBox();
+ nameTxt = new SuggestBox(new AccountGroupSuggestOracle(), nameTxtBox);
+ nameTxtBox.setVisibleLength(50);
+ nameTxtBox.setText(Util.C.defaultAccountGroupName());
+ nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ nameTxtBox.addFocusHandler(new FocusHandler() {
+ @Override
+ public void onFocus(FocusEvent event) {
+ if (Util.C.defaultAccountGroupName().equals(nameTxtBox.getText())) {
+ nameTxtBox.setText("");
+ nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ nameTxtBox.addBlurHandler(new BlurHandler() {
+ @Override
+ public void onBlur(BlurEvent event) {
+ if ("".equals(nameTxtBox.getText())) {
+ nameTxtBox.setText(Util.C.defaultAccountGroupName());
+ nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
+ }
+ }
+ });
+ addGrid.setText(1, 0, Util.C.columnGroupName() + ":");
+ addGrid.setWidget(1, 1, nameTxt);
+
+ addGrid.setText(2, 0, Util.C.columnRightRange() + ":");
+ addGrid.setWidget(2, 1, rangeMinBox);
+
+ addGrid.setText(3, 0, "");
+ addGrid.setWidget(3, 1, rangeMaxBox);
+
+ addRight = new Button(Util.C.buttonAddProjectRight());
+ addRight.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddNewRight();
+ }
+ });
+ addPanel.add(addGrid);
+ addPanel.add(addRight);
+
+ rights = new RightsTable();
+
+ delRight = new Button(Util.C.buttonDeleteGroupMembers());
+ delRight.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ rights.deleteChecked();
+ }
+ });
+
+ body.add(new SmallHeading(Util.C.headingAccessRights()));
+ body.add(rights);
+ body.add(delRight);
+ body.add(addPanel);
+ }
+
+ void display(final ProjectDetail result) {
+ rights.display(result.groups, result.rights);
+ }
+
+ private void doAddNewRight() {
+ int idx = catBox.getSelectedIndex();
+ final ApprovalType at;
+ ApprovalCategoryValue min, max;
+ if (idx < 0) {
+ return;
+ }
+ at =
+ Gerrit.getConfig().getApprovalTypes().getApprovalType(
+ new ApprovalCategory.Id(catBox.getValue(idx)));
+ if (at == null) {
+ return;
+ }
+
+ idx = rangeMinBox.getSelectedIndex();
+ if (idx < 0) {
+ return;
+ }
+ min = at.getValue(Short.parseShort(rangeMinBox.getValue(idx)));
+ if (min == null) {
+ return;
+ }
+
+ idx = rangeMaxBox.getSelectedIndex();
+ if (idx < 0) {
+ return;
+ }
+ max = at.getValue(Short.parseShort(rangeMaxBox.getValue(idx)));
+ if (max == null) {
+ return;
+ }
+
+ final String groupName = nameTxt.getText();
+ if ("".equals(groupName)
+ || Util.C.defaultAccountGroupName().equals(groupName)) {
+ return;
+ }
+
+ if (min.getValue() > max.getValue()) {
+ // If the user selects it backwards in the web UI, help them out
+ // by reversing the order to what we would expect.
+ //
+ final ApprovalCategoryValue newMin = max;
+ final ApprovalCategoryValue newMax = min;
+ min = newMin;
+ max = newMax;
+ }
+
+ addRight.setEnabled(false);
+ Util.PROJECT_SVC.addRight(projectName, at.getCategory().getId(), groupName,
+ min.getValue(), max.getValue(), new GerritCallback<ProjectDetail>() {
+ public void onSuccess(final ProjectDetail result) {
+ addRight.setEnabled(true);
+ nameTxt.setText("");
+ display(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ addRight.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void populateRangeBoxes() {
+ final int idx = catBox.getSelectedIndex();
+ final ApprovalType at;
+ if (idx >= 0) {
+ at =
+ Gerrit.getConfig().getApprovalTypes().getApprovalType(
+ new ApprovalCategory.Id(catBox.getValue(idx)));
+ } else {
+ at = null;
+ }
+
+ if (at != null && !at.getValues().isEmpty()) {
+ int curIndex = 0, minIndex = -1, maxIndex = -1;
+ rangeMinBox.clear();
+ rangeMaxBox.clear();
+ for (final ApprovalCategoryValue v : at.getValues()) {
+ final String vStr = String.valueOf(v.getValue());
+ String nStr = vStr + ": " + v.getName();
+ if (v.getValue() > 0) {
+ nStr = "+" + nStr;
+ }
+
+ rangeMinBox.addItem(nStr, vStr);
+ rangeMaxBox.addItem(nStr, vStr);
+
+ if (v.getValue() < 0) {
+ minIndex = curIndex;
+ }
+ if (maxIndex < 0 && v.getValue() > 0) {
+ maxIndex = curIndex;
+ }
+
+ curIndex++;
+ }
+ if (ApprovalCategory.READ.equals(at.getCategory().getId())) {
+ // Special case; for READ the most logical range is just
+ // +1 READ, so assume that as the default for both.
+ minIndex = maxIndex;
+ }
+ rangeMinBox.setSelectedIndex(minIndex >= 0 ? minIndex : 0);
+ rangeMaxBox.setSelectedIndex(maxIndex >= 0 ? maxIndex : curIndex - 1);
+ } else {
+ rangeMinBox.setEnabled(false);
+ rangeMaxBox.setEnabled(false);
+ }
+ }
+
+ private class RightsTable extends FancyFlexTable<ProjectRight> {
+ RightsTable() {
+ table.setWidth("");
+ table.setText(0, 2, Util.C.columnApprovalCategory());
+ table.setText(0, 3, Util.C.columnGroupName());
+ table.setText(0, 4, Util.C.columnRightRange());
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, 1, S_ICON_HEADER);
+ fmt.addStyleName(0, 2, S_DATA_HEADER);
+ fmt.addStyleName(0, 3, S_DATA_HEADER);
+ fmt.addStyleName(0, 4, S_DATA_HEADER);
+ }
+
+ void deleteChecked() {
+ final HashSet<ProjectRight.Key> ids = new HashSet<ProjectRight.Key>();
+ for (int row = 1; row < table.getRowCount(); row++) {
+ final ProjectRight k = getRowItem(row);
+ if (k != null && table.getWidget(row, 1) instanceof CheckBox
+ && ((CheckBox) table.getWidget(row, 1)).getValue()) {
+ ids.add(k.getKey());
+ }
+ }
+ if (!ids.isEmpty()) {
+ Util.PROJECT_SVC.deleteRight(projectName, ids,
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ for (int row = 1; row < table.getRowCount();) {
+ final ProjectRight k = getRowItem(row);
+ if (k != null && ids.contains(k.getKey())) {
+ table.removeRow(row);
+ } else {
+ row++;
+ }
+ }
+ }
+ });
+ }
+ }
+
+ void display(final Map<AccountGroup.Id, AccountGroup> groups,
+ final List<ProjectRight> result) {
+ while (1 < table.getRowCount())
+ table.removeRow(table.getRowCount() - 1);
+
+ for (final ProjectRight k : result) {
+ final int row = table.getRowCount();
+ table.insertRow(row);
+ applyDataRowStyle(row);
+ populate(row, groups, k);
+ }
+ }
+
+ void populate(final int row,
+ final Map<AccountGroup.Id, AccountGroup> groups, final ProjectRight k) {
+ final GerritConfig config = Gerrit.getConfig();
+ final ApprovalType ar =
+ config.getApprovalTypes().getApprovalType(k.getApprovalCategoryId());
+ final AccountGroup group = groups.get(k.getAccountGroupId());
+
+ if (Gerrit.getConfig().getWildProject().equals(k.getProjectNameKey())
+ && !Gerrit.getConfig().getWildProject().equals(projectName)) {
+ table.setText(row, 1, "");
+ } else {
+ table.setWidget(row, 1, new CheckBox());
+ }
+
+ if (ar != null) {
+ table.setText(row, 2, ar.getCategory().getName());
+ } else {
+ table.setText(row, 2, k.getApprovalCategoryId().get());
+ }
+
+ if (group != null) {
+ table.setText(row, 3, group.getName());
+ } else {
+ table.setText(row, 3, Util.M.deletedGroup(k.getAccountGroupId().get()));
+ }
+
+ {
+ final SafeHtmlBuilder m = new SafeHtmlBuilder();
+ final ApprovalCategoryValue min, max;
+ min = ar != null ? ar.getValue(k.getMinValue()) : null;
+ max = ar != null ? ar.getValue(k.getMaxValue()) : null;
+
+ formatValue(m, k.getMinValue(), min);
+ if (k.getMinValue() != k.getMaxValue()) {
+ m.br();
+ formatValue(m, k.getMaxValue(), max);
+ }
+ SafeHtml.set(table, row, 4, m);
+ }
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(row, 1, S_ICON_CELL);
+ fmt.addStyleName(row, 2, S_DATA_CELL);
+ fmt.addStyleName(row, 3, S_DATA_CELL);
+ fmt.addStyleName(row, 4, S_DATA_CELL);
+ fmt.addStyleName(row, 4, "gerrit-ProjectAdmin-ApprovalCategoryRangeLine");
+
+ setRowItem(row, k);
+ }
+
+ private void formatValue(final SafeHtmlBuilder m, final short v,
+ final ApprovalCategoryValue e) {
+ m.openSpan();
+ m.setStyleName("gerrit-ProjectAdmin-ApprovalCategoryValue");
+ if (v == 0) {
+ m.append(' ');
+ } else if (v > 0) {
+ m.append('+');
+ }
+ m.append(v);
+ m.closeSpan();
+ if (e != null) {
+ m.append(": ");
+ m.append(e.getName());
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
new file mode 100644
index 0000000000..41671161aa
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.admin;
+
+import com.google.gerrit.common.data.GroupAdminService;
+import com.google.gerrit.common.data.ProjectAdminService;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class Util {
+ public static final AdminConstants C = GWT.create(AdminConstants.class);
+ public static final AdminMessages M = GWT.create(AdminMessages.class);
+ public static final GroupAdminService GROUP_SVC;
+ public static final ProjectAdminService PROJECT_SVC;
+
+ static {
+ GROUP_SVC = GWT.create(GroupAdminService.class);
+ JsonUtil.bind(GROUP_SVC, "rpc/GroupAdminService");
+
+ PROJECT_SVC = GWT.create(ProjectAdminService.class);
+ JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
+ }
+
+ public static String toLongString(final Project.SubmitType type) {
+ if (type == null) {
+ return "";
+ }
+ switch (type) {
+ case FAST_FORWARD_ONLY:
+ return C.projectSubmitType_FAST_FORWARD_ONLY();
+ case MERGE_IF_NECESSARY:
+ return C.projectSubmitType_MERGE_IF_NECESSARY();
+ case MERGE_ALWAYS:
+ return C.projectSubmitType_MERGE_ALWAYS();
+ case CHERRY_PICK:
+ return C.projectSubmitType_CHERRY_PICK();
+ default:
+ return type.name();
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/arrowRight.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/client/arrowRight.gif
index d9e63a5754..d9e63a5754 100644
--- a/src/main/java/com/google/gerrit/client/arrowRight.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/arrowRight.gif
Binary files differ
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java
index a94a8cf5e4..a94a8cf5e4 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.java
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties
index 93310f02b2..93310f02b2 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java
index d555424e07..d555424e07 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginIcons.java
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java
index d117ca2196..d117ca2196 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.java
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties
index 0ee8223a77..0ee8223a77 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/LoginMessages.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java
new file mode 100644
index 0000000000..bcb793bdc7
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java
@@ -0,0 +1,333 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.auth.openid;
+
+import com.google.gerrit.client.SignInDialog;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.auth.openid.DiscoveryResult;
+import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.FormElement;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Cookies;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.Hidden;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+import java.util.Map;
+
+public class OpenIdSignInDialog extends SignInDialog implements
+ FormPanel.SubmitHandler {
+ private final LoginIcons icons;
+ private final FlowPanel panelWidget;
+ private final FormPanel form;
+ private final FlowPanel formBody;
+ private final FormPanel redirectForm;
+ private final FlowPanel redirectBody;
+
+ private FlowPanel errorLine;
+ private InlineLabel errorMsg;
+
+ private Button login;
+ private NpTextBox providerId;
+ private CheckBox rememberId;
+ private boolean discovering;
+
+ public OpenIdSignInDialog(final SignInMode requestedMode,
+ final String initialErrorMsg) {
+ super(requestedMode);
+
+ icons = GWT.create(LoginIcons.class);
+
+ formBody = new FlowPanel();
+ formBody.setStyleName("gerrit-OpenID-loginform");
+
+ form = new FormPanel();
+ form.setMethod(FormPanel.METHOD_GET);
+ form.addSubmitHandler(this);
+ form.add(formBody);
+
+ redirectBody = new FlowPanel();
+ redirectBody.setVisible(false);
+ redirectForm = new FormPanel();
+ redirectForm.add(redirectBody);
+
+ panelWidget = new FlowPanel();
+ panelWidget.add(form);
+ panelWidget.add(redirectForm);
+ add(panelWidget);
+
+ createHeaderLogo();
+ createHeaderText();
+ createErrorBox();
+ createIdentBox();
+
+ link(OpenIdUrls.URL_GOOGLE, OpenIdUtil.C.nameGoogle(), icons.iconGoogle());
+ link(OpenIdUrls.URL_YAHOO, OpenIdUtil.C.nameYahoo(), icons.iconYahoo());
+
+ if (initialErrorMsg != null) {
+ showError(initialErrorMsg);
+ }
+ formBody.add(new HTML(OpenIdUtil.C.whatIsOpenIDHtml()));
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ providerId.selectAll();
+ DeferredCommand.addCommand(new Command() {
+ @Override
+ public void execute() {
+ providerId.setFocus(true);
+ }
+ });
+ }
+
+ private void createHeaderLogo() {
+ final FlowPanel headerLogo = new FlowPanel();
+ headerLogo.setStyleName("gerrit-OpenID-logobox");
+ headerLogo.add(icons.openidLogo().createImage());
+ formBody.add(headerLogo);
+ }
+
+ private void createHeaderText() {
+ final FlowPanel headerText = new FlowPanel();
+ final String me = Window.Location.getHostName();
+ final SmallHeading headerLabel = new SmallHeading();
+ switch (mode) {
+ case LINK_IDENTIY:
+ headerLabel.setText(OpenIdUtil.M.linkAt(me));
+ break;
+ case REGISTER:
+ headerLabel.setText(OpenIdUtil.M.registerAt(me));
+ break;
+ case SIGN_IN:
+ default:
+ headerLabel.setText(OpenIdUtil.M.signInAt(me));
+ break;
+ }
+ headerText.add(headerLabel);
+ formBody.add(headerText);
+ }
+
+ private void createErrorBox() {
+ errorLine = new FlowPanel();
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
+ errorLine.setStyleName("gerrit-OpenID-errorline");
+
+ errorMsg = new InlineLabel();
+ errorLine.add(errorMsg);
+ formBody.add(errorLine);
+ }
+
+ private void showError(final String msgText) {
+ errorMsg.setText(msgText);
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "");
+ }
+
+ private void hideError() {
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
+ }
+
+ private void createIdentBox() {
+ final FlowPanel group = new FlowPanel();
+ group.setStyleName("gerrit-OpenID-loginline");
+
+ final FlowPanel line1 = new FlowPanel();
+ group.add(line1);
+
+ providerId = new NpTextBox();
+ providerId.setVisibleLength(60);
+ providerId.setStyleName("gerrit-OpenID-openid_identifier");
+ providerId.setTabIndex(0);
+ providerId.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+ event.preventDefault();
+ form.submit();
+ }
+ }
+ });
+ line1.add(providerId);
+
+ login = new Button();
+ switch (mode) {
+ case LINK_IDENTIY:
+ login.setText(OpenIdUtil.C.buttonLinkId());
+ break;
+ case REGISTER:
+ login.setText(OpenIdUtil.C.buttonRegister());
+ break;
+ case SIGN_IN:
+ default:
+ login.setText(OpenIdUtil.C.buttonSignIn());
+ break;
+ }
+ login.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ form.submit();
+ }
+ });
+ login.setTabIndex(2);
+ line1.add(login);
+
+ if (mode == SignInMode.SIGN_IN) {
+ rememberId = new CheckBox(OpenIdUtil.C.rememberMe());
+ rememberId.setTabIndex(1);
+ group.add(rememberId);
+
+ final String last = Cookies.getCookie(OpenIdUrls.LASTID_COOKIE);
+ if (last != null && !"".equals(last)) {
+ providerId.setText(last);
+ rememberId.setValue(true);
+ }
+ }
+
+ formBody.add(group);
+ }
+
+ private void link(final String identUrl, final String who,
+ final AbstractImagePrototype icon) {
+ final ClickHandler i = new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ if (!discovering) {
+ providerId.setText(identUrl);
+ form.submit();
+ }
+ }
+ };
+
+ final FlowPanel line = new FlowPanel();
+ line.addStyleName("gerrit-OpenID-directlink");
+
+ final Image img = icon.createImage();
+ img.addClickHandler(i);
+ line.add(img);
+
+ final InlineLabel lbl = new InlineLabel();
+ switch (mode) {
+ case LINK_IDENTIY:
+ lbl.setText(OpenIdUtil.M.linkWith(who));
+ break;
+ case REGISTER:
+ lbl.setText(OpenIdUtil.M.registerWith(who));
+ break;
+ case SIGN_IN:
+ default:
+ lbl.setText(OpenIdUtil.M.signInWith(who));
+ break;
+ }
+ lbl.addClickHandler(i);
+ line.add(lbl);
+
+ formBody.add(line);
+ }
+
+ private void enable(final boolean on) {
+ providerId.setEnabled(on);
+ login.setEnabled(on);
+ }
+
+ private void onDiscovery(final DiscoveryResult result) {
+ discovering = false;
+
+ if (result.validProvider) {
+ redirectForm.setMethod(FormPanel.METHOD_POST);
+ redirectForm.setAction(result.providerUrl);
+ redirectBody.clear();
+ for (final Map.Entry<String, String> e : result.providerArgs.entrySet()) {
+ redirectBody.add(new Hidden(e.getKey(), e.getValue()));
+ }
+
+ // The provider won't support operation inside an IFRAME, so we
+ // replace our entire application. No fancy waits are needed,
+ // the browser won't update anything until its started to load
+ // the provider's page.
+ //
+ FormElement.as(redirectForm.getElement()).setTarget("_top");
+ redirectForm.submit();
+
+ } else {
+ // We failed discovery. We have to use a deferred command here
+ // as we are being called from within an invisible IFRAME. Jump
+ // back to the main event loop in the parent window.
+ //
+ onDiscoveryFailure();
+ }
+ }
+
+ private void onDiscoveryFailure() {
+ showError(OpenIdUtil.C.notSupported());
+ enable(true);
+ providerId.selectAll();
+ providerId.setFocus(true);
+ }
+
+ @Override
+ public void onSubmit(final SubmitEvent event) {
+ event.cancel();
+
+ final String openidIdentifier = providerId.getText();
+ if (openidIdentifier == null || openidIdentifier.equals("")) {
+ enable(true);
+ return;
+ }
+
+ discovering = true;
+ enable(false);
+ hideError();
+
+ final boolean remember = rememberId != null && rememberId.getValue();
+ final String token = History.getToken();
+ OpenIdUtil.SVC.discover(openidIdentifier, mode, remember, token,
+ new GerritCallback<DiscoveryResult>() {
+ public void onSuccess(final DiscoveryResult result) {
+ onDiscovery(result);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ super.onFailure(caught);
+ onDiscoveryFailure();
+ }
+ });
+ }
+
+ public void onSubmitComplete(final FormSubmitCompleteEvent event) {
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
new file mode 100644
index 0000000000..879265d559
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.auth.openid;
+
+import com.google.gerrit.common.auth.openid.OpenIdService;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class OpenIdUtil {
+ public static final LoginConstants C;
+ public static final LoginMessages M;
+ public static final OpenIdService SVC;
+
+ static {
+ C = GWT.create(LoginConstants.class);
+ M = GWT.create(LoginMessages.class);
+ SVC = GWT.create(OpenIdService.class);
+ JsonUtil.bind(SVC, "rpc/OpenIdService");
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif
index 782c2fd2d8..782c2fd2d8 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconGoogle.gif
Binary files differ
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif
index 08204558ba..08204558ba 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/iconYahoo.gif
Binary files differ
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png
index 520ff9c77e..520ff9c77e 100644
--- a/src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/openidLogo.png
Binary files differ
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java
index cbaeab05c3..cbaeab05c3 100644
--- a/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.java
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties
index 91204f7bfb..91204f7bfb 100644
--- a/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java
index 6cdd5a6448..6cdd5a6448 100644
--- a/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.java
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties
index 61223e8956..61223e8956 100644
--- a/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/LoginMessages.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java
new file mode 100644
index 0000000000..a0ec038458
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java
@@ -0,0 +1,229 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.auth.userpass;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.SignInDialog;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.auth.userpass.LoginResult;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.Window.Location;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.PasswordTextBox;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
+
+public class UserPassSignInDialog extends SignInDialog {
+ private final FlowPanel formBody;
+
+ private FlowPanel errorLine;
+ private InlineLabel errorMsg;
+
+ private Button login;
+ private Button close;
+ private TextBox username;
+ private TextBox password;
+
+ public UserPassSignInDialog(final String initialErrorMsg) {
+ super(SignInMode.SIGN_IN);
+
+ formBody = new FlowPanel();
+ formBody.setStyleName("gerrit-OpenID-loginform");
+ add(formBody);
+
+ createHeaderText();
+ createErrorBox();
+ createUsernameBox();
+ if (initialErrorMsg != null) {
+ showError(initialErrorMsg);
+ }
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ DeferredCommand.addCommand(new Command() {
+ @Override
+ public void execute() {
+ username.setFocus(true);
+ }
+ });
+ }
+
+ private void createHeaderText() {
+ final FlowPanel headerText = new FlowPanel();
+ final SmallHeading headerLabel = new SmallHeading();
+ headerLabel.setText(Util.M.signInAt(Location.getHostName()));
+ headerText.add(headerLabel);
+ formBody.add(headerText);
+ }
+
+ private void createErrorBox() {
+ errorLine = new FlowPanel();
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
+ errorLine.setStyleName("gerrit-OpenID-errorline");
+
+ errorMsg = new InlineLabel();
+ errorLine.add(errorMsg);
+ formBody.add(errorLine);
+ }
+
+ private void showError(final String msgText) {
+ errorMsg.setText(msgText);
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "");
+ }
+
+ private void hideError() {
+ DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
+ }
+
+ private void createUsernameBox() {
+ username = new NpTextBox();
+ username.setVisibleLength(25);
+ username.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+ event.preventDefault();
+ password.selectAll();
+ password.setFocus(true);
+ }
+ }
+ });
+
+ password = new PasswordTextBox();
+ password.setVisibleLength(25);
+ password.addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
+ password.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (event.getCharCode() == KeyCodes.KEY_ENTER) {
+ event.preventDefault();
+ onLogin();
+ }
+ }
+ });
+
+ final FlowPanel buttons = new FlowPanel();
+ buttons.setStyleName("gerrit-ErrorDialog-Buttons");
+
+ login = new Button();
+ login.setText(Util.C.buttonSignIn());
+ login.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ onLogin();
+ }
+ });
+ buttons.add(login);
+
+ close = new Button();
+ close.setText(Gerrit.C.errorDialogClose());
+ close.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ buttons.add(close);
+
+ final Grid formGrid = new Grid(3, 2);
+ formGrid.setText(0, 0, Util.C.username());
+ formGrid.setText(1, 0, Util.C.password());
+ formGrid.setWidget(0, 1, username);
+ formGrid.setWidget(1, 1, password);
+ formGrid.setWidget(2, 1, buttons);
+ formBody.add(formGrid);
+
+ username.setTabIndex(1);
+ password.setTabIndex(2);
+ login.setTabIndex(3);
+ close.setTabIndex(4);
+ }
+
+ private void enable(final boolean on) {
+ username.setEnabled(on);
+ password.setEnabled(on);
+ login.setEnabled(on);
+ }
+
+ private void onLogin() {
+ hideError();
+
+ final String user = username.getText();
+ if (user == null || user.equals("")) {
+ showError(Util.C.usernameRequired());
+ username.setFocus(true);
+ return;
+ }
+
+ final String pass = password.getText();
+ if (pass == null || pass.equals("")) {
+ showError(Util.C.passwordRequired());
+ password.setFocus(true);
+ return;
+ }
+
+ enable(false);
+ Util.SVC.authenticate(user, pass, new GerritCallback<LoginResult>() {
+ public void onSuccess(final LoginResult result) {
+ if (result.success) {
+ String token = History.getToken();
+ if (result.isNew && !token.startsWith(PageLinks.REGISTER + ",")) {
+ token = PageLinks.REGISTER + "," + token;
+ }
+
+ // Unfortunately we no longer support updating the web UI when the
+ // user signs in. Instead we must force a reload of the page, but
+ // that isn't easy because we might need to change the anchor. So
+ // we bounce through a little redirection servlet on the server.
+ //
+ Location.replace(Location.getPath() + "login/" + token);
+ } else {
+ showError(Util.C.invalidLogin());
+ enable(true);
+ password.selectAll();
+ DeferredCommand.addCommand(new Command() {
+ @Override
+ public void execute() {
+ password.setFocus(true);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ super.onFailure(caught);
+ enable(false);
+ }
+ });
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/Util.java
new file mode 100644
index 0000000000..738eacd57a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/userpass/Util.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.auth.userpass;
+
+import com.google.gerrit.common.auth.userpass.UserPassAuthService;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class Util {
+ public static final LoginConstants C = GWT.create(LoginConstants.class);
+ public static final LoginMessages M = GWT.create(LoginMessages.class);
+ public static final UserPassAuthService SVC;
+
+ static {
+ SVC = GWT.create(UserPassAuthService.class);
+ JsonUtil.bind(SVC, "rpc/UserPassAuthService");
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java
new file mode 100644
index 0000000000..b046c22217
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java
@@ -0,0 +1,106 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+
+public class AbandonChangeDialog extends AutoCenterDialogBox {
+ private final FlowPanel panel;
+ private final NpTextArea message;
+ private final Button sendButton;
+ private final Button cancelButton;
+ private final PatchSet.Id psid;
+
+ public AbandonChangeDialog(final PatchSet.Id psi,
+ final AsyncCallback<ChangeDetail> callback) {
+ super(/* auto hide */true, /* modal */true);
+
+ psid = psi;
+ addStyleName("gerrit-AbandonChangeDialog");
+ setText(Util.C.abandonChangeTitle());
+
+ panel = new FlowPanel();
+ add(panel);
+
+ panel.add(new SmallHeading(Util.C.headingAbandonMessage()));
+
+ final FlowPanel mwrap = new FlowPanel();
+ mwrap.setStyleName("gerrit-AbandonMessage");
+ panel.add(mwrap);
+
+ message = new NpTextArea();
+ message.setCharacterWidth(60);
+ message.setVisibleLines(10);
+ DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
+ mwrap.add(message);
+
+ final FlowPanel buttonPanel = new FlowPanel();
+ buttonPanel.setStyleName("gerrit-CommentEditor-Buttons");
+ panel.add(buttonPanel);
+
+ sendButton = new Button(Util.C.buttonAbandonChangeSend());
+ sendButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ sendButton.setEnabled(false);
+ Util.MANAGE_SVC.abandonChange(psid, message.getText().trim(),
+ new GerritCallback<ChangeDetail>() {
+ public void onSuccess(ChangeDetail result) {
+ if (callback != null) {
+ callback.onSuccess(result);
+ }
+ hide();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ sendButton.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ buttonPanel.add(sendButton);
+
+ cancelButton = new Button(Util.C.buttonAbandonChangeCancel());
+ cancelButton.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ if (callback != null) {
+ callback.onFailure(null);
+ }
+ hide();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ message.setFocus(true);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
new file mode 100644
index 0000000000..7c3e3210a9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.changes.ChangeTable.ApprovalViewType;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AccountDashboardInfo;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.reviewdb.Account;
+
+
+public class AccountDashboardScreen extends Screen {
+ private final Account.Id ownerId;
+ private ChangeTable table;
+ private ChangeTable.Section byOwner;
+ private ChangeTable.Section forReview;
+ private ChangeTable.Section closed;
+
+ public AccountDashboardScreen(final Account.Id id) {
+ ownerId = id;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ table = new ChangeTable(true);
+ table.addStyleName("gerrit-AccountDashboard");
+ byOwner = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
+ forReview = new ChangeTable.Section("", ApprovalViewType.USER, ownerId);
+ closed = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
+
+ table.addSection(byOwner);
+ table.addSection(forReview);
+ table.addSection(closed);
+ add(table);
+ table.setSavePointerId(PageLinks.toAccountDashboard(ownerId));
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.LIST_SVC.forAccount(ownerId,
+ new ScreenLoadCallback<AccountDashboardInfo>(this) {
+ @Override
+ protected void preDisplay(final AccountDashboardInfo r) {
+ display(r);
+ }
+ });
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ table.setRegisterKeys(true);
+ }
+
+ private void display(final AccountDashboardInfo r) {
+ table.setAccountInfoCache(r.getAccounts());
+
+ final AccountInfo o = r.getAccounts().get(r.getOwner());
+ final String name = FormatUtil.name(o);
+ setWindowTitle(name);
+ setPageTitle(Util.M.accountDashboardTitle(name));
+ byOwner.setTitleText(Util.M.changesStartedBy(name));
+ forReview.setTitleText(Util.M.changesReviewableBy(name));
+ closed.setTitleText(Util.C.changesRecentlyClosed());
+
+ byOwner.display(r.getByOwner());
+ forReview.display(r.getForReview());
+ closed.display(r.getClosed());
+ table.finishDisplay();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java
new file mode 100644
index 0000000000..39b6162c8e
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.reviewdb.Change;
+
+
+public class AllAbandonedChangesScreen extends AllSingleListScreen {
+ public AllAbandonedChangesScreen(final String positionToken) {
+ super("all,abandoned", positionToken);
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setWindowTitle(Gerrit.C.menuAllAbandoned());
+ setPageTitle(Util.C.allAbandonedChanges());
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.allClosedPrev(Change.Status.ABANDONED, pos, pageSize,
+ loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.allClosedNext(Change.Status.ABANDONED, pos, pageSize,
+ loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java
new file mode 100644
index 0000000000..9c4931a35a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.reviewdb.Change;
+
+
+public class AllMergedChangesScreen extends AllSingleListScreen {
+ public AllMergedChangesScreen(final String positionToken) {
+ super("all,merged", positionToken);
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setWindowTitle(Gerrit.C.menuAllMerged());
+ setPageTitle(Util.C.allMergedChanges());
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.allClosedPrev(Change.Status.MERGED, pos, pageSize,
+ loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.allClosedNext(Change.Status.MERGED, pos, pageSize,
+ loadCallback());
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java
index 94050666b4..94050666b4 100644
--- a/src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllOpenChangesScreen.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java
new file mode 100644
index 0000000000..8ec4f971ca
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java
@@ -0,0 +1,162 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.SingleListChangeInfo;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Hyperlink;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+
+import java.util.List;
+
+
+public abstract class AllSingleListScreen extends Screen {
+ protected static final String MIN_SORTKEY = "";
+ protected static final String MAX_SORTKEY = "z";
+
+ protected final int pageSize;
+ private ChangeTable table;
+ private ChangeTable.Section section;
+ protected Hyperlink prev;
+ protected Hyperlink next;
+ protected List<ChangeInfo> changes;
+
+ protected final String anchorPrefix;
+ protected boolean useLoadPrev;
+ protected String pos;
+
+ protected AllSingleListScreen(final String anchorToken,
+ final String positionToken) {
+ anchorPrefix = anchorToken;
+ useLoadPrev = positionToken.startsWith("p,");
+ pos = positionToken.substring(2);
+
+ if (Gerrit.isSignedIn()) {
+ final AccountGeneralPreferences p =
+ Gerrit.getUserAccount().getGeneralPreferences();
+ final short m = p.getMaximumPageSize();
+ pageSize = 0 < m ? m : AccountGeneralPreferences.DEFAULT_PAGESIZE;
+ } else {
+ pageSize = AccountGeneralPreferences.DEFAULT_PAGESIZE;
+ }
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ prev = new Hyperlink(Util.C.pagedChangeListPrev(), true, "");
+ prev.setVisible(false);
+
+ next = new Hyperlink(Util.C.pagedChangeListNext(), true, "");
+ next.setVisible(false);
+
+ table = new ChangeTable() {
+ {
+ keysNavigation.add(new DoLinkCommand(0, 'p', Util.C
+ .changeTablePagePrev(), prev));
+ keysNavigation.add(new DoLinkCommand(0, 'n', Util.C
+ .changeTablePageNext(), next));
+ }
+ };
+ section = new ChangeTable.Section();
+
+ table.addSection(section);
+ table.setSavePointerId(anchorPrefix);
+ add(table);
+
+ final HorizontalPanel buttons = new HorizontalPanel();
+ buttons.setStyleName("gerrit-ChangeTable-PrevNextLinks");
+ buttons.add(prev);
+ buttons.add(next);
+ add(buttons);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ if (useLoadPrev) {
+ loadPrev();
+ } else {
+ loadNext();
+ }
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ table.setRegisterKeys(true);
+ }
+
+ protected abstract void loadPrev();
+
+ protected abstract void loadNext();
+
+ protected AsyncCallback<SingleListChangeInfo> loadCallback() {
+ return new ScreenLoadCallback<SingleListChangeInfo>(this) {
+ @Override
+ protected void preDisplay(final SingleListChangeInfo result) {
+ display(result);
+ }
+ };
+ }
+
+ protected void display(final SingleListChangeInfo result) {
+ changes = result.getChanges();
+
+ if (!changes.isEmpty()) {
+ final ChangeInfo f = changes.get(0);
+ final ChangeInfo l = changes.get(changes.size() - 1);
+
+ prev.setTargetHistoryToken(anchorPrefix + ",p," + f.getSortKey());
+ next.setTargetHistoryToken(anchorPrefix + ",n," + l.getSortKey());
+
+ if (useLoadPrev) {
+ prev.setVisible(!result.isAtEnd());
+ next.setVisible(!MIN_SORTKEY.equals(pos));
+ } else {
+ prev.setVisible(!MAX_SORTKEY.equals(pos));
+ next.setVisible(!result.isAtEnd());
+ }
+ }
+
+ table.setAccountInfoCache(result.getAccounts());
+ section.display(result.getChanges());
+ table.finishDisplay();
+ }
+
+ private static final class DoLinkCommand extends KeyCommand {
+ private final Hyperlink link;
+
+ private DoLinkCommand(int mask, char key, String help, Hyperlink l) {
+ super(mask, key, help);
+ link = l;
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (link.isVisible()) {
+ History.newItem(link.getTargetHistoryToken());
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
new file mode 100644
index 0000000000..bbdb228141
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
@@ -0,0 +1,284 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.ErrorDialog;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.patches.PatchUtil;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountDashboardLink;
+import com.google.gerrit.client.ui.AddMemberBox;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.AddReviewerResult;
+import com.google.gerrit.common.data.ApprovalDetail;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Displays a table of {@link ApprovalDetail} objects for a change record. */
+public class ApprovalTable extends Composite {
+ private final List<ApprovalType> types;
+ private final Grid table;
+ private final Widget missing;
+ private final Panel addReviewer;
+ private final AddMemberBox addMemberBox;
+ private Change.Id changeId;
+ private boolean changeIsOpen;
+ private AccountInfoCache accountCache = AccountInfoCache.empty();
+
+ public ApprovalTable() {
+ types = Gerrit.getConfig().getApprovalTypes().getApprovalTypes();
+ table = new Grid(1, 3 + types.size());
+ table.addStyleName("gerrit-InfoTable");
+ displayHeader();
+
+ missing = new Widget() {
+ {
+ setElement(DOM.createElement("ul"));
+ }
+ };
+ missing.setStyleName("gerrit-MissingApprovalList");
+
+ addReviewer = new FlowPanel();
+ addReviewer.setStyleName("gerrit-AddReviewer");
+ addMemberBox = new AddMemberBox();
+ addMemberBox.setAddButtonText(Util.C.approvalTableAddReviewer());
+ addMemberBox.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ doAddReviewer();
+ }
+ });
+ addReviewer.add(addMemberBox);
+ addReviewer.setVisible(false);
+
+ final FlowPanel fp = new FlowPanel();
+ fp.add(table);
+ fp.add(missing);
+ fp.add(addReviewer);
+ initWidget(fp);
+
+ setStyleName("gerrit-ApprovalTable");
+ }
+
+ private void displayHeader() {
+ int col = 0;
+ header(col++, Util.C.approvalTableReviewer());
+ header(col++, "");
+
+ for (final ApprovalType t : types) {
+ header(col++, t.getCategory().getName());
+ }
+ applyEdgeStyles(0);
+ }
+
+ private void header(final int col, final String title) {
+ table.setText(0, col, title);
+ table.getCellFormatter().addStyleName(0, col, "header");
+ }
+
+ private void applyEdgeStyles(final int row) {
+ final CellFormatter fmt = table.getCellFormatter();
+ fmt.addStyleName(row, 0, "leftmost");
+ fmt.addStyleName(row, 0, "reviewer");
+ fmt.addStyleName(row, 1, "approvalrole");
+ fmt.addStyleName(row, 1 + types.size(), "rightmost");
+ fmt.addStyleName(row, 2 + types.size(), "approvalhint");
+ }
+
+ private void applyScoreStyles(final int row) {
+ final CellFormatter fmt = table.getCellFormatter();
+ for (int col = 0; col < types.size(); col++) {
+ fmt.addStyleName(row, 2 + col, "approvalscore");
+ }
+ }
+
+ public void setAccountInfoCache(final AccountInfoCache aic) {
+ assert aic != null;
+ accountCache = aic;
+ }
+
+ private AccountDashboardLink link(final Account.Id id) {
+ return AccountDashboardLink.link(accountCache, id);
+ }
+
+ public void display(final Change change, final Set<ApprovalCategory.Id> need,
+ final List<ApprovalDetail> rows) {
+ changeId = change.getId();
+
+ final int oldcnt = table.getRowCount();
+ table.resizeRows(1 + rows.size());
+ if (oldcnt < 1 + rows.size()) {
+ for (int row = oldcnt; row < 1 + rows.size(); row++) {
+ applyEdgeStyles(row);
+ applyScoreStyles(row);
+ }
+ }
+
+ if (rows.isEmpty()) {
+ table.setVisible(false);
+ } else {
+ table.setVisible(true);
+ for (int i = 0; i < rows.size(); i++) {
+ displayRow(i + 1, rows.get(i));
+ }
+ }
+
+ final Element missingList = missing.getElement();
+ while (DOM.getChildCount(missingList) > 0) {
+ DOM.removeChild(missingList, DOM.getChild(missingList, 0));
+ }
+
+ missing.setVisible(false);
+ if (need != null) {
+ for (final ApprovalType at : types) {
+ if (need.contains(at.getCategory().getId())) {
+ final Element li = DOM.createElement("li");
+ li.setClassName("gerrit-MissingApproval");
+ DOM.setInnerText(li, Util.M.needApproval(at.getCategory().getName(),
+ at.getMax().formatValue(), at.getMax().getName()));
+ DOM.appendChild(missingList, li);
+ missing.setVisible(true);
+ }
+ }
+ }
+
+ changeIsOpen = change.getStatus().isOpen();
+ addReviewer.setVisible(Gerrit.isSignedIn() && changeIsOpen);
+ }
+
+ private void doAddReviewer() {
+ final String nameEmail = addMemberBox.getText();
+ if (nameEmail.length() == 0) {
+ return;
+ }
+
+ addMemberBox.setEnabled(false);
+ final List<String> reviewers = new ArrayList<String>();
+ reviewers.add(nameEmail);
+
+ PatchUtil.DETAIL_SVC.addReviewers(changeId, reviewers,
+ new GerritCallback<AddReviewerResult>() {
+ public void onSuccess(final AddReviewerResult result) {
+ addMemberBox.setEnabled(true);
+ addMemberBox.setText("");
+
+ if (!result.getErrors().isEmpty()) {
+ final SafeHtmlBuilder r = new SafeHtmlBuilder();
+ for (final AddReviewerResult.Error e : result.getErrors()) {
+ switch (e.getType()) {
+ case ACCOUNT_NOT_FOUND:
+ r.append(Util.M.accountNotFound(e.getName()));
+ break;
+
+ case CHANGE_NOT_VISIBLE:
+ r.append(Util.M.changeNotVisibleTo(e.getName()));
+ break;
+
+ default:
+ r.append(e.getName());
+ r.append(" - ");
+ r.append(e.getType());
+ r.br();
+ break;
+ }
+ }
+ new ErrorDialog(r).center();
+ }
+
+ final ChangeDetail r = result.getChange();
+ if (r != null) {
+ setAccountInfoCache(r.getAccounts());
+ display(r.getChange(), r.getMissingApprovals(), r.getApprovals());
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ addMemberBox.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void displayRow(final int row, final ApprovalDetail ad) {
+ final CellFormatter fmt = table.getCellFormatter();
+ final Map<ApprovalCategory.Id, PatchSetApproval> am = ad.getApprovalMap();
+ final StringBuilder hint = new StringBuilder();
+ int col = 0;
+ table.setWidget(row, col++, link(ad.getAccount()));
+ table.clearCell(row, col++); // TODO populate the account role
+
+ for (final ApprovalType type : types) {
+ final PatchSetApproval ca = am.get(type.getCategory().getId());
+ if (ca == null || ca.getValue() == 0) {
+ table.clearCell(row, col);
+ col++;
+ continue;
+ }
+
+ final ApprovalCategoryValue acv = type.getValue(ca);
+ if (acv != null) {
+ if (hint.length() > 0) {
+ hint.append("; ");
+ }
+ hint.append(acv.getName());
+ }
+
+ if (type.isMaxNegative(ca)) {
+ table.setWidget(row, col, Gerrit.ICONS.redNot().createImage());
+
+ } else if (type.isMaxPositive(ca)) {
+ table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
+
+ } else {
+ String vstr = String.valueOf(ca.getValue());
+ if (ca.getValue() > 0) {
+ vstr = "+" + vstr;
+ fmt.removeStyleName(row, col, "negscore");
+ fmt.addStyleName(row, col, "posscore");
+ } else {
+ fmt.addStyleName(row, col, "negscore");
+ fmt.removeStyleName(row, col, "posscore");
+ }
+ table.setText(row, col, vstr);
+ }
+
+ col++;
+ }
+
+ table.setText(row, col++, hint.toString());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java
new file mode 100644
index 0000000000..79d00ef9e7
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+
+
+public class ByProjectAbandonedChangesScreen extends AllSingleListScreen {
+ private final Project.NameKey projectKey;
+
+ public ByProjectAbandonedChangesScreen(final Project.NameKey proj,
+ final String positionToken) {
+ super("project,abandoned," + proj.toString(), positionToken);
+ projectKey = proj;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.M.changesAbandonedInProject(projectKey.get()));
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.byProjectClosedPrev(projectKey, Change.Status.ABANDONED, pos,
+ pageSize, loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.byProjectClosedNext(projectKey, Change.Status.ABANDONED, pos,
+ pageSize, loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java
new file mode 100644
index 0000000000..55caf90501
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+
+
+public class ByProjectMergedChangesScreen extends AllSingleListScreen {
+ private final Project.NameKey projectKey;
+
+ public ByProjectMergedChangesScreen(final Project.NameKey proj,
+ final String positionToken) {
+ super("project,merged," + proj.toString(), positionToken);
+ projectKey = proj;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.M.changesMergedInProject(projectKey.get()));
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.byProjectClosedPrev(projectKey, Change.Status.MERGED, pos,
+ pageSize, loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.byProjectClosedNext(projectKey, Change.Status.MERGED, pos,
+ pageSize, loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java
new file mode 100644
index 0000000000..f9525a47c9
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.reviewdb.Project;
+
+
+public class ByProjectOpenChangesScreen extends AllSingleListScreen {
+ private final Project.NameKey projectKey;
+
+ public ByProjectOpenChangesScreen(final Project.NameKey proj,
+ final String positionToken) {
+ super("project,open," + proj.toString(), positionToken);
+ projectKey = proj;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setPageTitle(Util.M.changesOpenInProject(projectKey.get()));
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.byProjectOpenPrev(projectKey, pos, pageSize, loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.byProjectOpenNext(projectKey, pos, pageSize, loadCallback());
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
index 4854fd9bdf..4854fd9bdf 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.java
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
index 73b9e94c62..73b9e94c62 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeConstants.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
new file mode 100644
index 0000000000..d3a943236d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+public class ChangeDescriptionBlock extends Composite {
+ private final ChangeInfoBlock infoBlock;
+ private final HTML description;
+
+ public ChangeDescriptionBlock() {
+ infoBlock = new ChangeInfoBlock();
+ description = new HTML();
+ description.setStyleName("gerrit-ChangeScreen-Description");
+
+ final HorizontalPanel hp = new HorizontalPanel();
+ hp.add(infoBlock);
+ hp.add(description);
+ initWidget(hp);
+ }
+
+ public void display(final Change chg, final PatchSetInfo info,
+ final AccountInfoCache acc) {
+ infoBlock.display(chg, acc);
+
+ SafeHtml msg = new SafeHtmlBuilder().append(info.getMessage());
+ msg = msg.linkify();
+ msg = msg.replaceAll(Gerrit.getConfig().getCommentLinks());
+ msg = new SafeHtmlBuilder().openElement("p").append(msg).closeElement("p");
+ msg = msg.replaceAll("\n\n", "</p><p>");
+ msg = msg.replaceAll("\n", "<br />");
+ SafeHtml.set(description, msg);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
new file mode 100644
index 0000000000..7a2cc640ab
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import static com.google.gerrit.client.FormatUtil.mediumFormat;
+
+import com.google.gerrit.client.ui.AccountDashboardLink;
+import com.google.gerrit.client.ui.ChangeLink;
+import com.google.gerrit.client.ui.ProjectLink;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
+
+public class ChangeInfoBlock extends Composite {
+ private static final int R_CHANGE_ID = 0;
+ private static final int R_OWNER = 1;
+ private static final int R_PROJECT = 2;
+ private static final int R_BRANCH = 3;
+ private static final int R_UPLOADED = 4;
+ private static final int R_UPDATED = 5;
+ private static final int R_STATUS = 6;
+ private static final int R_PERMALINK = 7;
+ private static final int R_CNT = 8;
+
+ private final Grid table;
+
+ public ChangeInfoBlock() {
+ table = new Grid(R_CNT, 2);
+ table.setStyleName("gerrit-InfoBlock");
+ table.addStyleName("gerrit-ChangeInfoBlock");
+
+ initRow(R_CHANGE_ID, "Change-Id: ");
+ initRow(R_OWNER, Util.C.changeInfoBlockOwner());
+ initRow(R_PROJECT, Util.C.changeInfoBlockProject());
+ initRow(R_BRANCH, Util.C.changeInfoBlockBranch());
+ initRow(R_UPLOADED, Util.C.changeInfoBlockUploaded());
+ initRow(R_UPDATED, Util.C.changeInfoBlockUpdated());
+ initRow(R_STATUS, Util.C.changeInfoBlockStatus());
+
+ final CellFormatter fmt = table.getCellFormatter();
+ fmt.addStyleName(0, 0, "topmost");
+ fmt.addStyleName(0, 1, "topmost");
+ fmt.addStyleName(R_CHANGE_ID, 1, "changeid");
+ fmt.addStyleName(R_CNT - 2, 0, "bottomheader");
+ fmt.addStyleName(R_PERMALINK, 0, "permalink");
+ fmt.addStyleName(R_PERMALINK, 1, "permalink");
+
+ initWidget(table);
+ }
+
+ private void initRow(final int row, final String name) {
+ table.setText(row, 0, name);
+ table.getCellFormatter().addStyleName(row, 0, "header");
+ }
+
+ public void display(final Change chg, final AccountInfoCache acc) {
+ final Branch.NameKey dst = chg.getDest();
+ table.setText(R_CHANGE_ID, 1, chg.getKey().get());
+ table.setWidget(R_OWNER, 1, AccountDashboardLink.link(acc, chg.getOwner()));
+ table.setWidget(R_PROJECT, 1, new ProjectLink(chg.getProject(), chg.getStatus()));
+ table.setText(R_BRANCH, 1, dst.getShortName());
+ table.setText(R_UPLOADED, 1, mediumFormat(chg.getCreatedOn()));
+ table.setText(R_UPDATED, 1, mediumFormat(chg.getLastUpdatedOn()));
+ table.setText(R_STATUS, 1, Util.toLongString(chg.getStatus()));
+
+ if (chg.getStatus().isClosed()) {
+ table.getCellFormatter().addStyleName(R_STATUS, 1, "closedstate");
+ } else {
+ table.getCellFormatter().removeStyleName(R_STATUS, 1, "closedstate");
+ }
+
+ final FlowPanel fp = new FlowPanel();
+ fp.add(new ChangeLink(Util.C.changePermalink(), chg.getId()));
+ fp.add(new CopyableLabel(ChangeLink.permalink(chg.getId()), false));
+ table.setWidget(R_PERMALINK, 1, fp);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
index 26ffe36a36..26ffe36a36 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.java
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
index 8f3def15be..8f3def15be 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages.properties
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties
index a5e9426b6d..a5e9426b6d 100644
--- a/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeMessages_en.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java
new file mode 100644
index 0000000000..001a06f3dc
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.SingleListChangeInfo;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.KeyUtil;
+
+
+
+public class ChangeQueryResultsScreen extends AllSingleListScreen {
+ private final String query;
+
+ public ChangeQueryResultsScreen(final String encQuery,
+ final String positionToken) {
+ super("q," + encQuery, positionToken);
+ query = KeyUtil.decode(encQuery);
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setWindowTitle(Util.M.changeQueryWindowTitle(query));
+ setPageTitle(Util.M.changeQueryPageTitle(query));
+ }
+
+ @Override
+ protected AsyncCallback<SingleListChangeInfo> loadCallback() {
+ return new GerritCallback<SingleListChangeInfo>() {
+ public final void onSuccess(final SingleListChangeInfo result) {
+ if (isAttached()) {
+ if (result.getChanges().size() == 1) {
+ final ChangeInfo c = result.getChanges().get(0);
+ Gerrit.display(PageLinks.toChange(c), new ChangeScreen(c));
+ } else {
+ Gerrit.setQueryString(query);
+ display(result);
+ ChangeQueryResultsScreen.this.display();
+ }
+ }
+ }
+ };
+ }
+
+ @Override
+ protected void loadPrev() {
+ Util.LIST_SVC.allQueryPrev(query, pos, pageSize, loadCallback());
+ }
+
+ @Override
+ protected void loadNext() {
+ Util.LIST_SVC.allQueryNext(query, pos, pageSize, loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
new file mode 100644
index 0000000000..e08f3302d1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
@@ -0,0 +1,402 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.CommentPanel;
+import com.google.gerrit.client.ui.ComplexDisclosurePanel;
+import com.google.gerrit.client.ui.ExpandAllCommand;
+import com.google.gerrit.client.ui.LinkMenuBar;
+import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.GitwebLink;
+import com.google.gerrit.common.data.ToggleStarRequest;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.DisclosurePanel;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+import com.google.gwtexpui.globalkey.client.KeyCommandSet;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+
+public class ChangeScreen extends Screen {
+ private final Change.Id changeId;
+
+ private Image starChange;
+ private boolean starred;
+ private PatchSet.Id currentPatchSet;
+ private ChangeDescriptionBlock descriptionBlock;
+ private ApprovalTable approvals;
+
+ private DisclosurePanel dependenciesPanel;
+ private ChangeTable dependencies;
+ private ChangeTable.Section dependsOn;
+ private ChangeTable.Section neededBy;
+
+ private FlowPanel patchSetPanels;
+
+ private Panel comments;
+
+ private KeyCommandSet keysNavigation;
+ private KeyCommandSet keysAction;
+ private HandlerRegistration regNavigation;
+ private HandlerRegistration regAction;
+
+ public ChangeScreen(final Change.Id toShow) {
+ changeId = toShow;
+ }
+
+ public ChangeScreen(final ChangeInfo c) {
+ this(c.getId());
+ }
+
+ @Override
+ public void onSignOut() {
+ super.onSignOut();
+ if (starChange != null) {
+ starChange.setVisible(false);
+ }
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ refresh();
+ }
+
+ @Override
+ protected void onUnload() {
+ if (regNavigation != null) {
+ regNavigation.removeHandler();
+ regNavigation = null;
+ }
+ if (regAction != null) {
+ regAction.removeHandler();
+ regAction = null;
+ }
+ super.onUnload();
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ regNavigation = GlobalKey.add(this, keysNavigation);
+ regAction = GlobalKey.add(this, keysAction);
+ }
+
+ public void refresh() {
+ Util.DETAIL_SVC.changeDetail(changeId,
+ new ScreenLoadCallback<ChangeDetail>(this) {
+ @Override
+ protected void preDisplay(final ChangeDetail r) {
+ display(r);
+ }
+ });
+ }
+
+ private void setStarred(final boolean s) {
+ if (s) {
+ Gerrit.ICONS.starFilled().applyTo(starChange);
+ } else {
+ Gerrit.ICONS.starOpen().applyTo(starChange);
+ }
+ starred = s;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ addStyleName("gerrit-ChangeScreen");
+
+ keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
+ keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
+ keysNavigation.add(new DashboardKeyCommand(0, 'u', Util.C.upToDashboard()));
+
+ if (Gerrit.isSignedIn()) {
+ keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar()));
+ keysAction.add(new PublishCommentsKeyCommand(0, 'r', Util.C
+ .keyPublishComments()));
+
+ starChange = Gerrit.ICONS.starOpen().createImage();
+ starChange.setStyleName("gerrit-ChangeScreen-StarIcon");
+ starChange.setVisible(Gerrit.isSignedIn());
+ starChange.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ toggleStar();
+ }
+ });
+ insertTitleWidget(starChange);
+ }
+
+ descriptionBlock = new ChangeDescriptionBlock();
+ add(descriptionBlock);
+
+ approvals = new ApprovalTable();
+ add(approvals);
+
+ dependencies = new ChangeTable();
+ dependsOn = new ChangeTable.Section(Util.C.changeScreenDependsOn());
+ neededBy = new ChangeTable.Section(Util.C.changeScreenNeededBy());
+ dependencies.addSection(dependsOn);
+ dependencies.addSection(neededBy);
+
+ dependenciesPanel = new DisclosurePanel(Util.C.changeScreenDependencies());
+ dependenciesPanel.setContent(dependencies);
+ dependenciesPanel.setWidth("95%");
+ add(dependenciesPanel);
+
+ patchSetPanels = new FlowPanel();
+ add(patchSetPanels);
+
+ comments = new FlowPanel();
+ comments.setStyleName("gerrit-ChangeComments");
+ add(comments);
+ }
+
+ private void displayTitle(final Change.Key changeId, final String subject) {
+ final StringBuilder titleBuf = new StringBuilder();
+ if (LocaleInfo.getCurrentLocale().isRTL()) {
+ if (subject != null) {
+ titleBuf.append(subject);
+ titleBuf.append(" :");
+ }
+ titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
+ } else {
+ titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
+ if (subject != null) {
+ titleBuf.append(": ");
+ titleBuf.append(subject);
+ }
+ }
+ setPageTitle(titleBuf.toString());
+ }
+
+ void display(final ChangeDetail detail) {
+ displayTitle(detail.getChange().getKey(), detail.getChange().getSubject());
+
+ if (starChange != null) {
+ setStarred(detail.isStarred());
+ }
+
+ dependencies.setAccountInfoCache(detail.getAccounts());
+ approvals.setAccountInfoCache(detail.getAccounts());
+
+ descriptionBlock.display(detail.getChange(), detail
+ .getCurrentPatchSetDetail().getInfo(), detail.getAccounts());
+ dependsOn.display(detail.getDependsOn());
+ neededBy.display(detail.getNeededBy());
+ approvals.display(detail.getChange(), detail.getMissingApprovals(), detail
+ .getApprovals());
+
+ addPatchSets(detail);
+ addComments(detail);
+
+ // If any dependency change is still open, show our dependency list.
+ //
+ boolean depsOpen = false;
+ if (!detail.getChange().getStatus().isClosed()
+ && detail.getDependsOn() != null) {
+ for (final ChangeInfo ci : detail.getDependsOn()) {
+ if (ci.getStatus() != Change.Status.MERGED) {
+ depsOpen = true;
+ break;
+ }
+ }
+ }
+
+ dependenciesPanel.setOpen(depsOpen);
+ }
+
+ private void addPatchSets(final ChangeDetail detail) {
+ patchSetPanels.clear();
+
+ final PatchSet currps = detail.getCurrentPatchSet();
+ final GitwebLink gw = Gerrit.getConfig().getGitwebLink();
+ for (final PatchSet ps : detail.getPatchSets()) {
+ final ComplexDisclosurePanel panel =
+ new ComplexDisclosurePanel(Util.M.patchSetHeader(ps.getPatchSetId()),
+ ps == currps);
+ final PatchSetPanel psp = new PatchSetPanel(this, detail, ps);
+ panel.setContent(psp);
+
+ final InlineLabel revtxt = new InlineLabel(ps.getRevision().get() + " ");
+ revtxt.addStyleName("gerrit-PatchSetRevision");
+ panel.getHeader().add(revtxt);
+ if (gw != null) {
+ final Anchor revlink =
+ new Anchor("(gitweb)", false, gw.toRevision(detail.getChange()
+ .getProject(), ps));
+ revlink.addStyleName("gerrit-PatchSetLink");
+ panel.getHeader().add(revlink);
+ }
+
+ if (ps == currps) {
+ psp.ensureLoaded(detail.getCurrentPatchSetDetail());
+ } else {
+ panel.addOpenHandler(psp);
+ }
+ add(panel);
+ patchSetPanels.add(panel);
+ }
+ currentPatchSet = currps.getId();
+ }
+
+ private void addComments(final ChangeDetail detail) {
+ comments.clear();
+
+ final Label hdr = new Label(Util.C.changeScreenComments());
+ hdr.setStyleName("gerrit-BlockHeader");
+ comments.add(hdr);
+
+ final AccountInfoCache accts = detail.getAccounts();
+ final List<ChangeMessage> msgList = detail.getMessages();
+ if (msgList.size() > 1) {
+ comments.add(messagesMenuBar());
+ }
+
+ final long AGE = 7 * 24 * 60 * 60 * 1000L;
+ final Timestamp aged = new Timestamp(System.currentTimeMillis() - AGE);
+
+ for (int i = 0; i < msgList.size(); i++) {
+ final ChangeMessage msg = msgList.get(i);
+
+ final AccountInfo author;
+ if (msg.getAuthor() != null) {
+ author = accts.get(msg.getAuthor());
+ } else {
+ final Account gerrit = new Account(null);
+ gerrit.setFullName(Util.C.messageNoAuthor());
+ author = new AccountInfo(gerrit);
+ }
+
+ boolean isRecent;
+ if (i == msgList.size() - 1) {
+ isRecent = true;
+ } else {
+ // TODO Instead of opening messages by strict age, do it by "unread"?
+ isRecent = msg.getWrittenOn().after(aged);
+ }
+
+ final CommentPanel cp =
+ new CommentPanel(author, msg.getWrittenOn(), msg.getMessage());
+ cp.setRecent(isRecent);
+ if (i == msgList.size() - 1) {
+ cp.addStyleName("gerrit-CommentPanel-Last");
+ cp.setOpen(true);
+ }
+ comments.add(cp);
+ }
+
+ if (msgList.size() > 1) {
+ comments.add(messagesMenuBar());
+ }
+ comments.setVisible(msgList.size() > 0);
+ }
+
+ private LinkMenuBar messagesMenuBar() {
+ final Panel c = comments;
+ final LinkMenuBar m = new LinkMenuBar();
+ m.addItem(Util.C.messageExpandRecent(), new ExpandAllCommand(c, true) {
+ @Override
+ protected void expand(final CommentPanel w) {
+ w.setOpen(w.isRecent());
+ }
+ });
+ m.addItem(Util.C.messageExpandAll(), new ExpandAllCommand(c, true));
+ m.addItem(Util.C.messageCollapseAll(), new ExpandAllCommand(c, false));
+ return m;
+ }
+
+ private void toggleStar() {
+ final boolean prior = starred;
+ setStarred(!prior);
+
+ final ToggleStarRequest req = new ToggleStarRequest();
+ req.toggle(changeId, starred);
+ Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ super.onFailure(caught);
+ setStarred(prior);
+ }
+ });
+ }
+
+ public class DashboardKeyCommand extends KeyCommand {
+ public DashboardKeyCommand(int mask, char key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (Gerrit.isSignedIn()) {
+ Gerrit.display(PageLinks.MINE, true);
+ } else {
+ Gerrit.display(PageLinks.ALL_OPEN, true);
+ }
+ }
+ }
+
+ public class StarKeyCommand extends NeedsSignInKeyCommand {
+ public StarKeyCommand(int mask, char key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ toggleStar();
+ }
+ }
+
+ public class PublishCommentsKeyCommand extends NeedsSignInKeyCommand {
+ public PublishCommentsKeyCommand(int mask, char key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ Gerrit.display("change,publish," + currentPatchSet.toString(),
+ new PublishCommentScreen(currentPatchSet));
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
new file mode 100644
index 0000000000..57259d529f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -0,0 +1,490 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import static com.google.gerrit.client.FormatUtil.shortFormat;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.patches.PatchUtil;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountDashboardLink;
+import com.google.gerrit.client.ui.ChangeLink;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
+import com.google.gerrit.client.ui.ProjectLink;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.ApprovalSummary;
+import com.google.gerrit.common.data.ApprovalSummarySet;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.ToggleStarRequest;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class ChangeTable extends NavigationTable<ChangeInfo> {
+ private static final String S_C_ID = "C_ID";
+ private static final String S_C_SUBJECT = "C_SUBJECT";
+ private static final String S_C_PROJECT = "C_PROJECT";
+ private static final String S_C_LAST_UPDATE = "C_LAST_UPDATE";
+ private static final String S_C_APPROVAL = "C_APPROVAL";
+ private static final String S_SECTION_HEADER = "SectionHeader";
+ private static final String S_EMPTY_SECTION = "EmptySection";
+ private static final String S_NEEDS_REVIEW = "NeedsReview";
+
+ private static final int C_STAR = 1;
+ private static final int C_ID = 2;
+ private static final int C_SUBJECT = 3;
+ private static final int C_OWNER = 4;
+ private static final int C_PROJECT = 5;
+ private static final int C_BRANCH = 6;
+ private static final int C_LAST_UPDATE = 7;
+ private static final int BASE_COLUMNS = 8;
+
+ private final List<Section> sections;
+ private AccountInfoCache accountCache = AccountInfoCache.empty();
+ private final List<ApprovalType> approvalTypes;
+ private final int columns;
+
+ public ChangeTable() {
+ this(false);
+ }
+
+ public ChangeTable(boolean showApprovals) {
+ approvalTypes = Gerrit.getConfig().getApprovalTypes().getApprovalTypes();
+ if (showApprovals) {
+ columns = BASE_COLUMNS + approvalTypes.size();
+ } else {
+ columns = BASE_COLUMNS;
+ }
+
+ keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.changeTablePrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.changeTableNext()));
+ keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.changeTableOpen()));
+ keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
+ .changeTableOpen()));
+
+ if (Gerrit.isSignedIn()) {
+ keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar()));
+ }
+
+ sections = new ArrayList<Section>();
+ table.setText(0, C_STAR, "");
+ table.setText(0, C_ID, Util.C.changeTableColumnID());
+ table.setText(0, C_SUBJECT, Util.C.changeTableColumnSubject());
+ table.setText(0, C_OWNER, Util.C.changeTableColumnOwner());
+ table.setText(0, C_PROJECT, Util.C.changeTableColumnProject());
+ table.setText(0, C_BRANCH, Util.C.changeTableColumnBranch());
+ table.setText(0, C_LAST_UPDATE, Util.C.changeTableColumnLastUpdate());
+ for (int i = BASE_COLUMNS; i < columns; i++) {
+ final ApprovalType type = approvalTypes.get(i - BASE_COLUMNS);
+ final ApprovalCategory cat = type.getCategory();
+ String text = cat.getAbbreviatedName();
+ if (text == null) {
+ text = cat.getName();
+ }
+ table.setText(0, i, text);
+ if (text != null) {
+ table.getCellFormatter().getElement(0, i).setTitle(cat.getName());
+ }
+ }
+
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.addStyleName(0, C_STAR, S_ICON_HEADER);
+ fmt.addStyleName(0, C_ID, S_C_ID);
+ for (int i = C_ID; i < columns; i++) {
+ fmt.addStyleName(0, i, S_DATA_HEADER);
+ }
+
+ table.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Cell cell = table.getCellForEvent(event);
+ if (cell == null) {
+ return;
+ }
+ if (cell.getCellIndex() == C_STAR) {
+ onStarClick(cell.getRowIndex());
+ } else if (cell.getCellIndex() == C_OWNER) {
+ // Don't do anything.
+ } else if (getRowItem(cell.getRowIndex()) != null) {
+ movePointerTo(cell.getRowIndex());
+ }
+ }
+ });
+ }
+
+ protected void onStarClick(final int row) {
+ final ChangeInfo c = getRowItem(row);
+ if (c != null && Gerrit.isSignedIn()) {
+ final boolean prior = c.isStarred();
+ c.setStarred(!prior);
+ setStar(row, c);
+
+ final ToggleStarRequest req = new ToggleStarRequest();
+ req.toggle(c.getId(), c.isStarred());
+ Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ super.onFailure(caught);
+ c.setStarred(prior);
+ setStar(row, c);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected Object getRowItemKey(final ChangeInfo item) {
+ return item.getId();
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ final ChangeInfo c = getRowItem(row);
+ Gerrit.display(PageLinks.toChange(c), new ChangeScreen(c));
+ }
+
+ private void insertNoneRow(final int row) {
+ insertRow(row);
+ table.setText(row, 0, Util.C.changeTableNone());
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.setColSpan(row, 0, columns);
+ fmt.setStyleName(row, 0, S_EMPTY_SECTION);
+ }
+
+ private void insertChangeRow(final int row) {
+ insertRow(row);
+ applyDataRowStyle(row);
+ }
+
+ @Override
+ protected void applyDataRowStyle(final int row) {
+ super.applyDataRowStyle(row);
+ final CellFormatter fmt = table.getCellFormatter();
+ fmt.addStyleName(row, C_STAR, S_ICON_CELL);
+ for (int i = C_ID; i < columns; i++) {
+ fmt.addStyleName(row, i, S_DATA_CELL);
+ }
+ fmt.addStyleName(row, C_ID, S_C_ID);
+ fmt.addStyleName(row, C_SUBJECT, S_C_SUBJECT);
+ fmt.addStyleName(row, C_PROJECT, S_C_PROJECT);
+ fmt.addStyleName(row, C_BRANCH, S_C_PROJECT);
+ fmt.addStyleName(row, C_LAST_UPDATE, S_C_LAST_UPDATE);
+ for (int i = BASE_COLUMNS; i < columns; i++) {
+ fmt.addStyleName(row, i, S_C_APPROVAL);
+ }
+ }
+
+ private void populateChangeRow(final int row, final ChangeInfo c) {
+ final String idstr = c.getKey().abbreviate();
+ table.setWidget(row, C_ARROW, null);
+ if (Gerrit.isSignedIn()) {
+ setStar(row, c);
+ }
+ table.setWidget(row, C_ID, new TableChangeLink(idstr, c));
+
+ String s = c.getSubject();
+ if (s.length() > 80) {
+ s = s.substring(0, 80);
+ }
+ if (c.getStatus() != null && c.getStatus() != Change.Status.NEW) {
+ s += " (" + c.getStatus().name() + ")";
+ }
+ table.setWidget(row, C_SUBJECT, new TableChangeLink(s, c));
+ table.setWidget(row, C_OWNER, link(c.getOwner()));
+ table.setWidget(row, C_PROJECT,
+ new ProjectLink(c.getProject().getKey(), c.getStatus()));
+ table.setText(row, C_BRANCH, c.getBranch());
+ table.setText(row, C_LAST_UPDATE, shortFormat(c.getLastUpdatedOn()));
+ setRowItem(row, c);
+ }
+
+ private AccountDashboardLink link(final Account.Id id) {
+ return AccountDashboardLink.link(accountCache, id);
+ }
+
+ private void setStar(final int row, final ChangeInfo c) {
+ final AbstractImagePrototype star;
+ if (c.isStarred()) {
+ star = Gerrit.ICONS.starFilled();
+ } else {
+ star = Gerrit.ICONS.starOpen();
+ }
+
+ final Widget i = table.getWidget(row, C_STAR);
+ if (i instanceof Image) {
+ star.applyTo((Image) i);
+ } else {
+ table.setWidget(row, C_STAR, star.createImage());
+ }
+ }
+
+ public void addSection(final Section s) {
+ assert s.parent == null;
+
+ if (s.titleText != null) {
+ s.titleRow = table.getRowCount();
+ table.setText(s.titleRow, 0, s.titleText);
+ final FlexCellFormatter fmt = table.getFlexCellFormatter();
+ fmt.setColSpan(s.titleRow, 0, columns);
+ fmt.addStyleName(s.titleRow, 0, S_SECTION_HEADER);
+ } else {
+ s.titleRow = -1;
+ }
+
+ s.parent = this;
+ s.dataBegin = table.getRowCount();
+ insertNoneRow(s.dataBegin);
+ sections.add(s);
+ }
+
+ public void setAccountInfoCache(final AccountInfoCache aic) {
+ assert aic != null;
+ accountCache = aic;
+ }
+
+ private int insertRow(final int beforeRow) {
+ for (final Section s : sections) {
+ if (beforeRow <= s.titleRow) {
+ s.titleRow++;
+ }
+ if (beforeRow < s.dataBegin) {
+ s.dataBegin++;
+ }
+ }
+ return table.insertRow(beforeRow);
+ }
+
+ private void removeRow(final int row) {
+ for (final Section s : sections) {
+ if (row < s.titleRow) {
+ s.titleRow--;
+ }
+ if (row < s.dataBegin) {
+ s.dataBegin--;
+ }
+ }
+ table.removeRow(row);
+ }
+
+ private void displayApprovals(final int row, final ApprovalSummary summary,
+ final AccountInfoCache aic, final boolean highlightUnreviewed) {
+ final CellFormatter fmt = table.getCellFormatter();
+ final Map<ApprovalCategory.Id, PatchSetApproval> approvals =
+ summary.getApprovalMap();
+ int col = BASE_COLUMNS;
+ boolean haveReview = false;
+
+ for (final ApprovalType type : approvalTypes) {
+ final PatchSetApproval ca = approvals.get(type.getCategory().getId());
+
+ fmt.removeStyleName(row, col, "negscore");
+ fmt.removeStyleName(row, col, "posscore");
+
+ if (ca == null || ca.getValue() == 0) {
+ table.clearCell(row, col);
+
+ } else {
+ haveReview = true;
+
+ if (type.isMaxNegative(ca)) {
+ table.setWidget(row, col, Gerrit.ICONS.redNot().createImage());
+
+ } else if (type.isMaxPositive(ca)) {
+ table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
+
+ } else {
+ String vstr = String.valueOf(ca.getValue());
+ if (ca.getValue() > 0) {
+ vstr = "+" + vstr;
+ fmt.addStyleName(row, col, "posscore");
+ } else {
+ fmt.addStyleName(row, col, "negscore");
+ }
+ table.setText(row, col, vstr);
+ }
+
+ final ApprovalCategoryValue acv = type.getValue(ca);
+ final AccountInfo ai = aic.get(ca.getAccountId());
+
+ // Some web browsers ignore the embedded newline; some like it;
+ // so we include a space before the newline to accommodate both.
+ //
+ fmt.getElement(row, col).setTitle(
+ acv.getName() + " \nby " + FormatUtil.nameEmail(ai));
+ }
+
+ col++;
+ }
+
+ final Element tr = DOM.getParent(fmt.getElement(row, 0));
+ UIObject.setStyleName(tr, S_NEEDS_REVIEW, !haveReview
+ && highlightUnreviewed);
+ }
+
+ GerritCallback<ApprovalSummarySet> approvalFormatter(final int dataBegin,
+ final int rows, final boolean highlightUnreviewed) {
+ return new GerritCallback<ApprovalSummarySet>() {
+ @Override
+ public void onSuccess(final ApprovalSummarySet as) {
+ Map<Change.Id, ApprovalSummary> ids = as.getSummaryMap();
+ AccountInfoCache aic = as.getAccountInfoCache();
+ for (int row = dataBegin; row < dataBegin + rows; row++) {
+ final ChangeInfo c = getRowItem(row);
+ if (ids.containsKey(c.getId())) {
+ displayApprovals(row, ids.get(c.getId()), aic, highlightUnreviewed);
+ }
+ }
+ }
+ };
+ }
+
+ public class StarKeyCommand extends NeedsSignInKeyCommand {
+ public StarKeyCommand(int mask, char key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ onStarClick(getCurrentRow());
+ }
+ }
+
+ private final class TableChangeLink extends ChangeLink {
+ private TableChangeLink(final String text, final ChangeInfo c) {
+ super(text, c);
+ }
+
+ @Override
+ public void go() {
+ movePointerTo(id);
+ super.go();
+ }
+ }
+
+ public enum ApprovalViewType {
+ NONE, USER, STRONGEST
+ }
+
+ public static class Section {
+ String titleText;
+
+ ChangeTable parent;
+ final ApprovalViewType viewType;
+ final Account.Id ownerId;
+ int titleRow = -1;
+ int dataBegin;
+ int rows;
+
+ public Section() {
+ this(null, ApprovalViewType.NONE, null);
+ }
+
+ public Section(final String titleText) {
+ this(titleText, ApprovalViewType.NONE, null);
+ }
+
+ public Section(final String titleText, final ApprovalViewType view,
+ final Account.Id owner) {
+ setTitleText(titleText);
+ viewType = view;
+ ownerId = owner;
+ }
+
+ public void setTitleText(final String text) {
+ titleText = text;
+ if (titleRow >= 0) {
+ parent.table.setText(titleRow, 0, titleText);
+ }
+ }
+
+ public void display(final List<ChangeInfo> changeList) {
+ final int sz = changeList != null ? changeList.size() : 0;
+ final boolean hadData = rows > 0;
+
+ if (hadData) {
+ while (sz < rows) {
+ parent.removeRow(dataBegin);
+ rows--;
+ }
+ }
+
+ if (sz == 0) {
+ if (hadData) {
+ parent.insertNoneRow(dataBegin);
+ }
+ } else {
+ Set<Change.Id> cids = new HashSet<Change.Id>();
+
+ if (!hadData) {
+ parent.removeRow(dataBegin);
+ }
+
+ while (rows < sz) {
+ parent.insertChangeRow(dataBegin + rows);
+ rows++;
+ }
+
+ for (int i = 0; i < sz; i++) {
+ ChangeInfo c = changeList.get(i);
+ parent.populateChangeRow(dataBegin + i, c);
+ cids.add(c.getId());
+ }
+
+ switch (viewType) {
+ case NONE:
+ break;
+ case USER:
+ PatchUtil.DETAIL_SVC.userApprovals(cids, ownerId, parent
+ .approvalFormatter(dataBegin, rows, true));
+ break;
+ case STRONGEST:
+ PatchUtil.DETAIL_SVC.strongestApprovals(cids, parent
+ .approvalFormatter(dataBegin, rows, false));
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MessagePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
new file mode 100644
index 0000000000..638cdec237
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+public class MessagePanel extends Composite {
+ boolean isRecent;
+
+ public MessagePanel(final ChangeMessage msg) {
+ final Widget l =
+ new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify()
+ .replaceAll(Gerrit.getConfig().getCommentLinks()).toBlockWidget();
+ l.setStyleName("gerrit-ChangeMessage-Message");
+ initWidget(l);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java
new file mode 100644
index 0000000000..2b935ba4b5
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.PageLinks;
+
+
+public class MineDraftsScreen extends MineSingleListScreen {
+ public MineDraftsScreen() {
+ super(PageLinks.MINE_DRAFTS);
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setWindowTitle(Gerrit.C.menyMyDrafts());
+ setPageTitle(Util.C.draftsHeading());
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.LIST_SVC.myDraftChanges(loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java
new file mode 100644
index 0000000000..9016b21fdc
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.common.data.SingleListChangeInfo;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+
+public abstract class MineSingleListScreen extends AccountScreen {
+ private final String anchor;
+ private ChangeTable table;
+ private ChangeTable.Section drafts;
+
+ protected MineSingleListScreen(final String historyToken) {
+ anchor = historyToken;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ table = new ChangeTable();
+ drafts = new ChangeTable.Section();
+
+ table.addSection(drafts);
+ table.setSavePointerId(anchor);
+
+ add(table);
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ table.setRegisterKeys(true);
+ }
+
+ protected AsyncCallback<SingleListChangeInfo> loadCallback() {
+ return new ScreenLoadCallback<SingleListChangeInfo>(this) {
+ @Override
+ protected void preDisplay(final SingleListChangeInfo result) {
+ display(result);
+ }
+ };
+ }
+
+ private void display(final SingleListChangeInfo result) {
+ table.setAccountInfoCache(result.getAccounts());
+ drafts.display(result.getChanges());
+ table.finishDisplay();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java
new file mode 100644
index 0000000000..df4d30d40a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.PageLinks;
+
+
+public class MineStarredScreen extends MineSingleListScreen {
+ public MineStarredScreen() {
+ super(PageLinks.MINE_STARRED);
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ setWindowTitle(Gerrit.C.menuMyStarredChanges());
+ setPageTitle(Util.C.starredHeading());
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.LIST_SVC.myStarredChanges(loadCallback());
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
new file mode 100644
index 0000000000..ab2b84615a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
@@ -0,0 +1,311 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.AccountDashboardLink;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.UserIdentity;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.OpenEvent;
+import com.google.gwt.event.logical.shared.OpenHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.DisclosurePanel;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
+
+import java.util.Collections;
+import java.util.Set;
+
+class PatchSetPanel extends Composite implements OpenHandler<DisclosurePanel> {
+ private static final int R_AUTHOR = 0;
+ private static final int R_COMMITTER = 1;
+ private static final int R_DOWNLOAD = 2;
+ private static final int R_CNT = 3;
+
+ private final ChangeScreen changeScreen;
+ private final ChangeDetail changeDetail;
+ private final PatchSet patchSet;
+ private final FlowPanel body;
+
+ private Grid infoTable;
+ private Panel actionsPanel;
+ private PatchTable patchTable;
+
+ PatchSetPanel(final ChangeScreen parent, final ChangeDetail detail,
+ final PatchSet ps) {
+ changeScreen = parent;
+ changeDetail = detail;
+ patchSet = ps;
+ body = new FlowPanel();
+ initWidget(body);
+ }
+
+ /**
+ * Display the table showing the Author, Committer and Download links,
+ * followed by the action buttons.
+ */
+ public void ensureLoaded(final PatchSetDetail detail) {
+ infoTable = new Grid(R_CNT, 2);
+ infoTable.setStyleName("gerrit-InfoBlock");
+ infoTable.addStyleName("gerrit-PatchSetInfoBlock");
+
+ initRow(R_AUTHOR, Util.C.patchSetInfoAuthor());
+ initRow(R_COMMITTER, Util.C.patchSetInfoCommitter());
+ initRow(R_DOWNLOAD, Util.C.patchSetInfoDownload());
+
+ final CellFormatter itfmt = infoTable.getCellFormatter();
+ itfmt.addStyleName(0, 0, "topmost");
+ itfmt.addStyleName(0, 1, "topmost");
+ itfmt.addStyleName(R_CNT - 1, 0, "bottomheader");
+ itfmt.addStyleName(R_AUTHOR, 1, "useridentity");
+ itfmt.addStyleName(R_COMMITTER, 1, "useridentity");
+ itfmt.addStyleName(R_DOWNLOAD, 1, "command");
+
+ final PatchSetInfo info = detail.getInfo();
+ displayUserIdentity(R_AUTHOR, info.getAuthor());
+ displayUserIdentity(R_COMMITTER, info.getCommitter());
+ displayDownload();
+
+
+ patchTable = new PatchTable();
+ patchTable.setSavePointerId("PatchTable " + patchSet.getId());
+ patchTable.display(info.getKey(), detail.getPatches());
+
+ body.add(infoTable);
+
+ actionsPanel = new FlowPanel();
+ actionsPanel.setStyleName("gerrit-PatchSetActions");
+ body.add(actionsPanel);
+ if (Gerrit.isSignedIn()) {
+ populateCommentAction();
+ if (changeDetail.isCurrentPatchSet(detail)) {
+ populateActions(detail);
+ }
+ }
+ body.add(patchTable);
+ }
+
+ private void displayDownload() {
+ final Branch.NameKey branchKey = changeDetail.getChange().getDest();
+ final Project.NameKey projectKey = changeDetail.getChange().getProject();
+ final String projectName = projectKey.get();
+ final FlowPanel downloads = new FlowPanel();
+
+ if (Gerrit.getConfig().isUseRepoDownload()) {
+ // This site prefers usage of the 'repo' tool, so suggest
+ // that for easy fetch.
+ //
+ final StringBuilder r = new StringBuilder();
+ r.append("repo download ");
+ r.append(projectName);
+ r.append(" ");
+ r.append(changeDetail.getChange().getChangeId());
+ r.append("/");
+ r.append(patchSet.getPatchSetId());
+ downloads.add(new CopyableLabel(r.toString()));
+ }
+
+ if (changeDetail.isAllowsAnonymous()
+ && Gerrit.getConfig().getGitDaemonUrl() != null) {
+ // Anonymous Git is claimed to be available, and this project
+ // isn't secured. The anonymous Git daemon will be much more
+ // efficient than our own SSH daemon, so prefer offering it.
+ //
+ final StringBuilder r = new StringBuilder();
+ r.append("git pull ");
+ r.append(Gerrit.getConfig().getGitDaemonUrl());
+ r.append(projectName);
+ r.append(" ");
+ r.append(patchSet.getRefName());
+ downloads.add(new CopyableLabel(r.toString()));
+
+ } else if (Gerrit.isSignedIn() && Gerrit.getUserAccount() != null
+ && Gerrit.getConfig().getSshdAddress() != null
+ && Gerrit.getUserAccount().getSshUserName() != null
+ && Gerrit.getUserAccount().getSshUserName().length() > 0) {
+ // The user is signed in and anonymous access isn't allowed.
+ // Use our SSH daemon URL as its the only way they can get
+ // to the project (that we know of anyway).
+ //
+ final String sshAddr = Gerrit.getConfig().getSshdAddress();
+ final StringBuilder r = new StringBuilder();
+ r.append("git pull ssh://");
+ r.append(Gerrit.getUserAccount().getSshUserName());
+ r.append("@");
+ if (sshAddr.startsWith("*:") || "".equals(sshAddr)) {
+ r.append(Window.Location.getHostName());
+ }
+ r.append(sshAddr);
+ r.append("/");
+ r.append(projectName);
+ r.append(" ");
+ r.append(patchSet.getRefName());
+ downloads.add(new CopyableLabel(r.toString()));
+ }
+
+ infoTable.setWidget(R_DOWNLOAD, 1, downloads);
+ }
+
+ private void displayUserIdentity(final int row, final UserIdentity who) {
+ if (who == null) {
+ infoTable.clearCell(row, 1);
+ return;
+ }
+
+ final FlowPanel fp = new FlowPanel();
+ fp.setStyleName("gerrit-PatchSetUserIdentity");
+ if (who.getName() != null) {
+ final Account.Id aId = who.getAccount();
+ if (aId != null) {
+ fp.add(new AccountDashboardLink(who.getName(), aId));
+ } else {
+ final InlineLabel lbl = new InlineLabel(who.getName());
+ lbl.setStyleName("gerrit-AccountName");
+ fp.add(lbl);
+ }
+ }
+ if (who.getEmail() != null) {
+ fp.add(new InlineLabel("<" + who.getEmail() + ">"));
+ }
+ if (who.getDate() != null) {
+ fp.add(new InlineLabel(FormatUtil.mediumFormat(who.getDate())));
+ }
+ infoTable.setWidget(row, 1, fp);
+ }
+
+ private void populateActions(final PatchSetDetail detail) {
+ final boolean isOpen = changeDetail.getChange().getStatus().isOpen();
+ Set<ApprovalCategory.Id> allowed = changeDetail.getCurrentActions();
+ if (allowed == null) {
+ allowed = Collections.emptySet();
+ }
+
+ if (isOpen && allowed.contains(ApprovalCategory.SUBMIT)) {
+ final Button b =
+ new Button(Util.M
+ .submitPatchSet(detail.getPatchSet().getPatchSetId()));
+ b.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ b.setEnabled(false);
+ Util.MANAGE_SVC.submit(patchSet.getId(),
+ new GerritCallback<ChangeDetail>() {
+ public void onSuccess(ChangeDetail result) {
+ onSubmitResult(result);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ b.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+ });
+ actionsPanel.add(b);
+ }
+
+ if (changeDetail.canAbandon()) {
+ final Button b = new Button(Util.C.buttonAbandonChangeBegin());
+ b.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ new AbandonChangeDialog(patchSet.getId(),
+ new AsyncCallback<ChangeDetail>() {
+ public void onSuccess(ChangeDetail result) {
+ changeScreen.display(result);
+ }
+
+ public void onFailure(Throwable caught) {
+ b.setEnabled(true);
+ }
+ }).center();
+ }
+ });
+ actionsPanel.add(b);
+ }
+ }
+
+ private void populateCommentAction() {
+ final Button b = new Button(Util.C.buttonPublishCommentsBegin());
+ b.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ Gerrit.display("change,publish," + patchSet.getId().toString(),
+ new PublishCommentScreen(patchSet.getId()));
+ }
+ });
+ actionsPanel.add(b);
+ }
+
+ @Override
+ public void onOpen(final OpenEvent<DisclosurePanel> event) {
+ if (infoTable == null) {
+ Util.DETAIL_SVC.patchSetDetail(patchSet.getId(),
+ new GerritCallback<PatchSetDetail>() {
+ public void onSuccess(final PatchSetDetail result) {
+ ensureLoaded(result);
+ }
+ });
+ }
+ }
+
+ private void initRow(final int row, final String name) {
+ infoTable.setText(row, 0, name);
+ infoTable.getCellFormatter().addStyleName(row, 0, "header");
+ }
+
+ private void onSubmitResult(final ChangeDetail result) {
+ if (result.getChange().getStatus() == Change.Status.NEW) {
+ // The submit failed. Try to locate the message and display
+ // it to the user, it should be the last one created by Gerrit.
+ //
+ ChangeMessage msg = null;
+ if (result.getMessages() != null && result.getMessages().size() > 0) {
+ for (int i = result.getMessages().size() - 1; i >= 0; i--) {
+ if (result.getMessages().get(i).getAuthor() == null) {
+ msg = result.getMessages().get(i);
+ break;
+ }
+ }
+ }
+
+ if (msg != null) {
+ new SubmitFailureDialog(result, msg).center();
+ }
+ }
+ changeScreen.display(result);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
new file mode 100644
index 0000000000..cbb29cdb66
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
@@ -0,0 +1,560 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.patches.PatchScreen;
+import com.google.gerrit.client.ui.DirectScreenLink;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.client.ui.PatchLink;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Patch.Key;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.IncrementalCommand;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.Cell;
+import com.google.gwtexpui.progress.client.ProgressBar;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+import com.google.gwtorm.client.KeyUtil;
+
+import java.util.List;
+
+public class PatchTable extends Composite {
+ private final FlowPanel myBody;
+ private PatchSet.Id psid;
+ private Command onLoadCommand;
+ private MyTable myTable;
+ private String savePointerId;
+ private List<Patch> patchList;
+
+ public PatchTable() {
+ myBody = new FlowPanel();
+ initWidget(myBody);
+ }
+
+ public void display(final PatchSet.Id id, final List<Patch> list) {
+ psid = id;
+ myTable = null;
+ patchList = list;
+
+ final DisplayCommand cmd = new DisplayCommand(list);
+ if (cmd.execute()) {
+ cmd.initMeter();
+ DeferredCommand.addCommand(cmd);
+ } else {
+ cmd.showTable();
+ }
+ }
+
+ public void setSavePointerId(final String id) {
+ savePointerId = id;
+ }
+
+ public boolean isLoaded() {
+ return myTable != null;
+ }
+
+ public void onTableLoaded(final Command cmd) {
+ if (myTable != null) {
+ cmd.execute();
+ } else {
+ onLoadCommand = cmd;
+ }
+ }
+
+ public void setRegisterKeys(final boolean on) {
+ myTable.setRegisterKeys(on);
+ }
+
+ public void movePointerTo(final Patch.Key k) {
+ myTable.movePointerTo(k);
+ }
+
+ public void notifyDraftDelta(final Patch.Key k, final int delta) {
+ if (myTable != null) {
+ myTable.notifyDraftDelta(k, delta);
+ }
+ }
+
+ /**
+ * @return a link to the previous file in this patch set, or null.
+ */
+ public DirectScreenLink getPreviousPatchLink(int index, PatchScreen.Type patchType) {
+ if (0 < index)
+ return createLink(index - 1, patchType, SafeHtml.asis(Util.C
+ .prevPatchLinkIcon()), null);
+ return null;
+ }
+
+ /**
+ * @return a link to the next file in this patch set, or null.
+ */
+ public DirectScreenLink getNextPatchLink(int index, PatchScreen.Type patchType) {
+ if (index < patchList.size() - 1)
+ return createLink(index + 1, patchType, null, SafeHtml.asis(Util.C
+ .nextPatchLinkIcon()));
+ return null;
+ }
+
+ /**
+ * @return a link to the the given patch.
+ * @param index The patch to link to
+ * @param patchType The type of patch display
+ * @param before A string to display at the beginning of the href text
+ * @param after A string to display at the end of the href text
+ */
+ private PatchLink createLink(int index, PatchScreen.Type patchType,
+ SafeHtml before, SafeHtml after) {
+ Patch patch = patchList.get(index);
+ Key thisKey = patch.getKey();
+ PatchLink link;
+ if (patchType == PatchScreen.Type.SIDE_BY_SIDE
+ && patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ link = new PatchLink.SideBySide("", thisKey, index, this);
+ } else {
+ link = new PatchLink.Unified("", thisKey, index, this);
+ }
+ SafeHtmlBuilder text = new SafeHtmlBuilder();
+ text.append(before);
+ text.append(getFileNameOnly(patch));
+ text.append(after);
+ SafeHtml.set(link, text);
+ return link;
+ }
+
+ private static String getFileNameOnly(Patch patch) {
+ // Note: use '/' here and not File.pathSeparator since git paths
+ // are always separated by /
+ //
+ String fileName = patch.getFileName();
+ int s = fileName.lastIndexOf('/');
+ if (s >= 0) {
+ fileName = fileName.substring(s + 1);
+ }
+ return fileName;
+ }
+
+ /**
+ * Update the reviewed status for the given patch.
+ */
+ public void updateReviewedStatus(Patch.Key patchKey, boolean reviewed) {
+ if (myTable != null) {
+ myTable.updateReviewedStatus(patchKey, reviewed);
+ }
+ }
+
+ private class MyTable extends NavigationTable<Patch> {
+ private static final int C_PATH = 2;
+ private static final int C_DRAFT = 3;
+ private static final int C_SIDEBYSIDE = 4;
+
+ MyTable() {
+ keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.patchTablePrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.patchTableNext()));
+ keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.patchTableOpen()));
+ keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
+ .patchTableOpen()));
+
+ table.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Cell cell = table.getCellForEvent(event);
+ if (cell != null && cell.getRowIndex() > 0) {
+ movePointerTo(cell.getRowIndex());
+ }
+ }
+ });
+ setSavePointerId(PatchTable.this.savePointerId);
+ }
+
+ void updateReviewedStatus(final Patch.Key patchKey, boolean reviewed) {
+ final int row = findRow(patchKey);
+ if (0 <= row) {
+ final Patch patch = getRowItem(row);
+ if (patch != null) {
+ patch.setReviewedByCurrentUser(reviewed);
+
+ int col = C_SIDEBYSIDE + 2;
+ if (patch.getPatchType() == Patch.PatchType.BINARY) {
+ col = C_SIDEBYSIDE + 3;
+ }
+
+ if (reviewed) {
+ table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
+ } else {
+ table.clearCell(row, col);
+ }
+ }
+ }
+ }
+
+ void notifyDraftDelta(final Patch.Key key, final int delta) {
+ final int row = findRow(key);
+ if (0 <= row) {
+ final Patch p = getRowItem(row);
+ if (p != null) {
+ p.setDraftCount(p.getDraftCount() + delta);
+ final SafeHtmlBuilder m = new SafeHtmlBuilder();
+ appendCommentCount(m, p);
+ SafeHtml.set(table, row, C_DRAFT, m);
+ }
+ }
+ }
+
+ @Override
+ public void resetHtml(final SafeHtml html) {
+ super.resetHtml(html);
+ }
+
+ @Override
+ public void movePointerTo(Object oldId) {
+ super.movePointerTo(oldId);
+ }
+
+ void initializeRow(int row) {
+ Patch patch = PatchTable.this.patchList.get(row - 1);
+ setRowItem(row, patch);
+
+ Widget nameCol;
+ if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ nameCol = new PatchLink.SideBySide(patch.getFileName(), patch.getKey(), row - 1,
+ PatchTable.this);
+ } else {
+ nameCol = new PatchLink.Unified(patch.getFileName(), patch.getKey(), row - 1,
+ PatchTable.this);
+ }
+ if (patch.getSourceFileName() != null) {
+ final String text;
+ if (patch.getChangeType() == Patch.ChangeType.RENAMED) {
+ text = Util.M.renamedFrom(patch.getSourceFileName());
+ } else if (patch.getChangeType() == Patch.ChangeType.COPIED) {
+ text = Util.M.copiedFrom(patch.getSourceFileName());
+ } else {
+ text = Util.M.otherFrom(patch.getSourceFileName());
+ }
+ final Label line = new Label(text);
+ line.setStyleName("SourceFilePath");
+ final FlowPanel cell = new FlowPanel();
+ cell.add(nameCol);
+ cell.add(line);
+ nameCol = cell;
+ }
+ table.setWidget(row, C_PATH, nameCol);
+
+ int C_UNIFIED = C_SIDEBYSIDE + 1;
+ if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
+ table.setWidget(row, C_SIDEBYSIDE,
+ new PatchLink.SideBySide(Util.C.patchTableDiffSideBySide(), patch.getKey(), row - 1,
+ PatchTable.this));
+
+ } else if (patch.getPatchType() == Patch.PatchType.BINARY) {
+ C_UNIFIED = C_SIDEBYSIDE + 2;
+ }
+ table.setWidget(row, C_UNIFIED,
+ new PatchLink.Unified(Util.C.patchTableDiffUnified(), patch.getKey(), row - 1,
+ PatchTable.this));
+ }
+
+ void appendHeader(final SafeHtmlBuilder m) {
+ m.openTr();
+
+ // Cursor
+ m.openTd();
+ m.addStyleName(S_ICON_HEADER);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ // Mode
+ m.openTd();
+ m.setStyleName(S_ICON_HEADER);
+ m.nbsp();
+ m.closeTd();
+
+ // "File path"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(Util.C.patchTableColumnName());
+ m.closeTd();
+
+ // "Comments"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(Util.C.patchTableColumnComments());
+ m.closeTd();
+
+ // "Diff"
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.setAttribute("colspan", 3);
+ m.append(Util.C.patchTableColumnDiff());
+ m.closeTd();
+
+ // "Reviewed"
+ if (Gerrit.isSignedIn()) {
+ m.openTd();
+ m.setStyleName(S_ICON_HEADER);
+ m.append(Util.C.reviewed());
+ m.closeTd();
+ }
+
+ m.closeTr();
+ }
+
+ void appendRow(final SafeHtmlBuilder m, final Patch p) {
+ m.openTr();
+
+ m.openTd();
+ m.addStyleName(S_ICON_CELL);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName("ChangeTypeCell");
+ m.append(p.getChangeType().getCode());
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("FilePathCell");
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("CommentCell");
+ appendCommentCount(m, p);
+ m.closeTd();
+
+ switch (p.getPatchType()) {
+ case UNIFIED:
+ openlink(m, 2);
+ m.closeTd();
+ break;
+
+ case BINARY: {
+ String base = GWT.getHostPageBaseURL();
+ base += "cat/" + KeyUtil.encode(p.getKey().toString());
+ switch (p.getChangeType()) {
+ case DELETED:
+ case MODIFIED:
+ openlink(m, 1);
+ m.openAnchor();
+ m.setAttribute("href", base + "^1");
+ m.append(Util.C.patchTableDownloadPreImage());
+ closelink(m);
+ break;
+ default:
+ emptycell(m, 1);
+ break;
+ }
+ switch (p.getChangeType()) {
+ case MODIFIED:
+ case ADDED:
+ openlink(m, 1);
+ m.openAnchor();
+ m.setAttribute("href", base + "^0");
+ m.append(Util.C.patchTableDownloadPostImage());
+ closelink(m);
+ break;
+ default:
+ emptycell(m, 1);
+ break;
+ }
+ break;
+ }
+
+ default:
+ emptycell(m, 2);
+ break;
+ }
+
+ openlink(m, 1);
+ m.closeTd();
+
+ // Green check mark if the user is logged in and they reviewed that file
+ if (Gerrit.isSignedIn()) {
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ if (p.isReviewedByCurrentUser()) {
+ m.append(SafeHtml.asis(Gerrit.ICONS.greenCheck().getHTML()));
+ }
+ m.closeTd();
+ }
+
+ m.closeTr();
+ }
+
+ void appendCommentCount(final SafeHtmlBuilder m, final Patch p) {
+ if (p.getCommentCount() > 0) {
+ m.append(Util.M.patchTableComments(p.getCommentCount()));
+ }
+ if (p.getDraftCount() > 0) {
+ if (p.getCommentCount() > 0) {
+ m.append(", ");
+ }
+ m.openSpan();
+ m.setStyleName("Drafts");
+ m.append(Util.M.patchTableDrafts(p.getDraftCount()));
+ m.closeSpan();
+ }
+ }
+
+ private void openlink(final SafeHtmlBuilder m, final int colspan) {
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("DiffLinkCell");
+ m.setAttribute("colspan", colspan);
+ }
+
+ private void closelink(final SafeHtmlBuilder m) {
+ m.closeAnchor();
+ m.closeTd();
+ }
+
+ private void emptycell(final SafeHtmlBuilder m, final int colspan) {
+ m.openTd();
+ m.addStyleName(S_DATA_CELL);
+ m.addStyleName("DiffLinkCell");
+ m.setAttribute("colspan", colspan);
+ m.nbsp();
+ m.closeTd();
+ }
+
+ @Override
+ protected Object getRowItemKey(final Patch item) {
+ return item.getKey();
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ Widget link = table.getWidget(row, C_PATH);
+ if (link instanceof FlowPanel) {
+ link = ((FlowPanel) link).getWidget(0);
+ }
+ if (link instanceof DirectScreenLink) {
+ ((DirectScreenLink) link).go();
+ }
+ }
+ }
+
+ private final class DisplayCommand implements IncrementalCommand {
+ private final MyTable table;
+ private final List<Patch> list;
+ private boolean attached;
+ private SafeHtmlBuilder nc = new SafeHtmlBuilder();
+ private int stage = 0;
+ private int row;
+ private double start;
+ private ProgressBar meter;
+
+ private DisplayCommand(final List<Patch> list) {
+ this.table = new MyTable();
+ this.list = list;
+ }
+
+ /**
+ * Add the files contained in the list of patches to the table, one per row.
+ */
+ @SuppressWarnings("fallthrough")
+ public boolean execute() {
+ final boolean attachedNow = isAttached();
+ if (!attached && attachedNow) {
+ // Remember that we have been attached at least once. If
+ // later we find we aren't attached we should stop running.
+ //
+ attached = true;
+ } else if (attached && !attachedNow) {
+ // If the user navigated away, we aren't in the DOM anymore.
+ // Don't continue to render.
+ //
+ return false;
+ }
+
+ start = System.currentTimeMillis();
+ switch (stage) {
+ case 0:
+ if (row == 0) {
+ table.appendHeader(nc);
+ }
+ while (row < list.size()) {
+ table.appendRow(nc, list.get(row));
+ if ((++row % 10) == 0 && longRunning()) {
+ updateMeter();
+ return true;
+ }
+ }
+ table.resetHtml(nc);
+ nc = null;
+ stage = 1;
+ row = 0;
+
+ case 1:
+ while (row < list.size()) {
+ table.initializeRow(row + 1);
+ if ((++row % 50) == 0 && longRunning()) {
+ updateMeter();
+ return true;
+ }
+ }
+ updateMeter();
+ showTable();
+ }
+ return false;
+ }
+
+ void showTable() {
+ PatchTable.this.myBody.clear();
+ PatchTable.this.myBody.add(table);
+ PatchTable.this.myTable = table;
+ table.finishDisplay();
+ if (PatchTable.this.onLoadCommand != null) {
+ PatchTable.this.onLoadCommand.execute();
+ PatchTable.this.onLoadCommand = null;
+ }
+ }
+
+ void initMeter() {
+ if (meter == null) {
+ meter = new ProgressBar(Util.M.loadingPatchSet(psid.get()));
+ PatchTable.this.myBody.clear();
+ PatchTable.this.myBody.add(meter);
+ }
+ updateMeter();
+ }
+
+ void updateMeter() {
+ if (meter != null) {
+ final int n = list.size();
+ meter.setValue(((100 * (stage * n + row)) / (2 * n)));
+ }
+ }
+
+ private boolean longRunning() {
+ return System.currentTimeMillis() - start > 200;
+ }
+ }
+
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
new file mode 100644
index 0000000000..73e6d1e81b
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
@@ -0,0 +1,326 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.patches.CommentEditorPanel;
+import com.google.gerrit.client.patches.PatchUtil;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.ui.AccountScreen;
+import com.google.gerrit.client.ui.PatchLink;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.RadioButton;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class PublishCommentScreen extends AccountScreen implements ClickHandler {
+ private static SavedState lastState;
+
+ private final PatchSet.Id patchSetId;
+ private Collection<ValueRadioButton> approvalButtons;
+ private ChangeDescriptionBlock descBlock;
+ private Panel approvalPanel;
+ private NpTextArea message;
+ private Panel draftsPanel;
+ private Button send;
+ private Button cancel;
+ private boolean saveStateOnUnload = true;
+ private List<CommentEditorPanel> commentEditors;
+
+ public PublishCommentScreen(final PatchSet.Id psi) {
+ patchSetId = psi;
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+ addStyleName("gerrit-PublishCommentsScreen");
+
+ approvalButtons = new ArrayList<ValueRadioButton>();
+ descBlock = new ChangeDescriptionBlock();
+ add(descBlock);
+
+ final FormPanel form = new FormPanel();
+ final FlowPanel body = new FlowPanel();
+ form.setWidget(body);
+ form.addSubmitHandler(new FormPanel.SubmitHandler() {
+ @Override
+ public void onSubmit(final SubmitEvent event) {
+ event.cancel();
+ }
+ });
+ add(form);
+
+ approvalPanel = new FlowPanel();
+ body.add(approvalPanel);
+ initMessage(body);
+
+ draftsPanel = new FlowPanel();
+ body.add(draftsPanel);
+
+ final FlowPanel buttonRow = new FlowPanel();
+ buttonRow.setStyleName("gerrit-CommentEditor-Buttons");
+ body.add(buttonRow);
+
+ send = new Button(Util.C.buttonPublishCommentsSend());
+ send.addClickHandler(this);
+ buttonRow.add(send);
+
+ cancel = new Button(Util.C.buttonPublishCommentsCancel());
+ cancel.addClickHandler(this);
+ buttonRow.add(cancel);
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ Util.DETAIL_SVC.patchSetPublishDetail(patchSetId,
+ new ScreenLoadCallback<PatchSetPublishDetail>(this) {
+ @Override
+ protected void preDisplay(final PatchSetPublishDetail result) {
+ send.setEnabled(true);
+ display(result);
+ }
+
+ @Override
+ protected void postDisplay() {
+ message.setFocus(true);
+ }
+ });
+ }
+
+ @Override
+ protected void onUnload() {
+ super.onUnload();
+ lastState = saveStateOnUnload ? new SavedState(this) : null;
+ }
+
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Widget sender = (Widget) event.getSource();
+ if (send == sender) {
+ onSend();
+ } else if (cancel == sender) {
+ saveStateOnUnload = false;
+ goChange();
+ }
+ }
+
+ private void initMessage(final Panel body) {
+ body.add(new SmallHeading(Util.C.headingCoverMessage()));
+
+ final VerticalPanel mwrap = new VerticalPanel();
+ mwrap.setStyleName("gerrit-CoverMessage");
+ body.add(mwrap);
+
+ message = new NpTextArea();
+ message.setCharacterWidth(60);
+ message.setVisibleLines(10);
+ DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
+ mwrap.add(message);
+ }
+
+ private void initApprovals(final PatchSetPublishDetail r, final Panel body) {
+ for (final ApprovalType ct : Gerrit.getConfig().getApprovalTypes()
+ .getApprovalTypes()) {
+ if (r.isAllowed(ct.getCategory().getId())) {
+ initApprovalType(r, body, ct);
+ }
+ }
+ }
+
+ private void initApprovalType(final PatchSetPublishDetail r,
+ final Panel body, final ApprovalType ct) {
+ body.add(new SmallHeading(ct.getCategory().getName() + ":"));
+
+ final VerticalPanel vp = new VerticalPanel();
+ vp.setStyleName("gerrit-ApprovalCategoryList");
+ final List<ApprovalCategoryValue> lst =
+ new ArrayList<ApprovalCategoryValue>(ct.getValues());
+ Collections.reverse(lst);
+ final ApprovalCategory.Id catId = ct.getCategory().getId();
+ final Set<ApprovalCategoryValue.Id> allowed = r.getAllowed(catId);
+ final PatchSetApproval prior = r.getChangeApproval(catId);
+
+ for (final ApprovalCategoryValue buttonValue : lst) {
+ if (!allowed.contains(buttonValue.getId())) {
+ continue;
+ }
+
+ final ValueRadioButton b =
+ new ValueRadioButton(buttonValue, ct.getCategory().getName());
+ b.setText(buttonValue.format());
+
+ if (lastState != null && patchSetId.equals(lastState.patchSetId)
+ && lastState.approvals.containsKey(buttonValue.getCategoryId())) {
+ b.setValue(lastState.approvals.get(buttonValue.getCategoryId()).equals(
+ buttonValue));
+ } else {
+ b.setValue(prior != null ? buttonValue.getValue() == prior.getValue()
+ : buttonValue.getValue() == 0);
+ }
+
+ approvalButtons.add(b);
+ vp.add(b);
+ }
+ body.add(vp);
+ }
+
+ private void display(final PatchSetPublishDetail r) {
+ setPageTitle(Util.M.publishComments(r.getChange().getKey().abbreviate(),
+ patchSetId.get()));
+ descBlock.display(r.getChange(), r.getPatchSetInfo(), r.getAccounts());
+
+ if (r.getChange().getStatus().isOpen()) {
+ initApprovals(r, approvalPanel);
+ }
+ if (lastState != null && patchSetId.equals(lastState.patchSetId)) {
+ message.setText(lastState.message);
+ }
+
+ draftsPanel.clear();
+ commentEditors = new ArrayList<CommentEditorPanel>();
+
+ if (!r.getDrafts().isEmpty()) {
+ draftsPanel.add(new SmallHeading(Util.C.headingPatchComments()));
+
+ Panel panel = null;
+ String priorFile = "";
+ for (final PatchLineComment c : r.getDrafts()) {
+ final Patch.Key patchKey = c.getKey().getParentKey();
+ final String fn = patchKey.get();
+ if (!fn.equals(priorFile)) {
+ panel = new FlowPanel();
+ panel.addStyleName("gerrit-PatchComments");
+ draftsPanel.add(panel);
+ // Parent table can be null here since we are not showing any
+ // next/previous links
+ panel.add(new PatchLink.SideBySide(fn, patchKey, 0, null /*
+ * parent
+ * table
+ */));
+ priorFile = fn;
+ }
+
+ final CommentEditorPanel editor = new CommentEditorPanel(c);
+ editor.setAuthorNameText(Util.M.lineHeader(c.getLine()));
+ editor.setOpen(true);
+ commentEditors.add(editor);
+ panel.add(editor);
+ }
+ }
+ }
+
+ private void onSend() {
+ if (commentEditors.isEmpty()) {
+ onSend2();
+ } else {
+ final GerritCallback<VoidResult> afterSaveDraft =
+ new GerritCallback<VoidResult>() {
+ private int done;
+
+ @Override
+ public void onSuccess(final VoidResult result) {
+ if (++done == commentEditors.size()) {
+ onSend2();
+ }
+ }
+ };
+ for (final CommentEditorPanel p : commentEditors) {
+ p.saveDraft(afterSaveDraft);
+ }
+ }
+ }
+
+ private void onSend2() {
+ final Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> values =
+ new HashMap<ApprovalCategory.Id, ApprovalCategoryValue.Id>();
+ for (final ValueRadioButton b : approvalButtons) {
+ if (b.getValue()) {
+ values.put(b.value.getCategoryId(), b.value.getId());
+ }
+ }
+
+ PatchUtil.DETAIL_SVC.publishComments(patchSetId, message.getText().trim(),
+ new HashSet<ApprovalCategoryValue.Id>(values.values()),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ saveStateOnUnload = false;
+ goChange();
+ }
+ });
+ }
+
+ private void goChange() {
+ final Change.Id ck = patchSetId.getParentKey();
+ Gerrit.display(PageLinks.toChange(ck), new ChangeScreen(ck));
+ }
+
+ private static class ValueRadioButton extends RadioButton {
+ final ApprovalCategoryValue value;
+
+ ValueRadioButton(final ApprovalCategoryValue v, final String label) {
+ super(label);
+ value = v;
+ }
+ }
+
+ private static class SavedState {
+ final PatchSet.Id patchSetId;
+ final String message;
+ final Map<ApprovalCategory.Id, ApprovalCategoryValue> approvals;
+
+ SavedState(final PublishCommentScreen p) {
+ patchSetId = p.patchSetId;
+ message = p.message.getText();
+ approvals = new HashMap<ApprovalCategory.Id, ApprovalCategoryValue>();
+ for (final ValueRadioButton b : p.approvalButtons) {
+ if (b.getValue()) {
+ approvals.put(b.value.getCategoryId(), b.value);
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java
new file mode 100644
index 0000000000..99a7db27a6
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+import com.google.gwtexpui.user.client.AutoCenterDialogBox;
+
+class SubmitFailureDialog extends AutoCenterDialogBox {
+ SubmitFailureDialog(final ChangeDetail result, final ChangeMessage msg) {
+ setText(Util.C.submitFailed());
+
+ final FlowPanel body = new FlowPanel();
+ final Widget msgText =
+ new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify()
+ .toBlockWidget();
+ body.add(msgText);
+
+ final FlowPanel buttonPanel = new FlowPanel();
+ buttonPanel.setStyleName("gerrit-CommentEditor-Buttons");
+ Button close = new Button(Util.C.buttonClose());
+ close.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ buttonPanel.add(close);
+ body.add(buttonPanel);
+
+ add(body);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
new file mode 100644
index 0000000000..0ae020aff8
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.changes;
+
+import com.google.gerrit.common.data.ChangeDetailService;
+import com.google.gerrit.common.data.ChangeListService;
+import com.google.gerrit.common.data.ChangeManageService;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class Util {
+ public static final ChangeConstants C = GWT.create(ChangeConstants.class);
+ public static final ChangeMessages M = GWT.create(ChangeMessages.class);
+
+ public static final ChangeDetailService DETAIL_SVC;
+ public static final ChangeListService LIST_SVC;
+ public static final ChangeManageService MANAGE_SVC;
+
+ static {
+ DETAIL_SVC = GWT.create(ChangeDetailService.class);
+ JsonUtil.bind(DETAIL_SVC, "rpc/ChangeDetailService");
+
+ LIST_SVC = GWT.create(ChangeListService.class);
+ JsonUtil.bind(LIST_SVC, "rpc/ChangeListService");
+
+ MANAGE_SVC = GWT.create(ChangeManageService.class);
+ JsonUtil.bind(MANAGE_SVC, "rpc/ChangeManageService");
+ }
+
+ public static String toLongString(final Change.Status status) {
+ if (status == null) {
+ return "";
+ }
+ switch (status) {
+ case NEW:
+ return C.statusLongNew();
+ case SUBMITTED:
+ return C.statusLongSubmitted();
+ case MERGED:
+ return C.statusLongMerged();
+ case ABANDONED:
+ return C.statusLongAbandoned();
+ default:
+ return status.name();
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/greenCheck.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png
index cd70687c82..cd70687c82 100644
--- a/src/main/java/com/google/gerrit/client/greenCheck.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/greenCheck.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
new file mode 100644
index 0000000000..b40c775e27
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
@@ -0,0 +1,610 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.changes.PatchTable;
+import com.google.gerrit.client.changes.PublishCommentScreen;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.ui.CommentPanel;
+import com.google.gerrit.client.ui.NavigationTable;
+import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.SparseFileContent;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+import com.google.gwtexpui.globalkey.client.KeyCommandSet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class AbstractPatchContentTable extends NavigationTable<Object> {
+ protected PatchTable fileList;
+ protected AccountInfoCache accountCache = AccountInfoCache.empty();
+ protected Patch.Key patchKey;
+ protected PatchSet.Id idSideA;
+ protected PatchSet.Id idSideB;
+ protected boolean onlyOneHunk;
+ protected String formatLanguage;
+
+ private final KeyCommandSet keysComment;
+ private HandlerRegistration regComment;
+
+ protected AbstractPatchContentTable() {
+ keysNavigation.add(new PrevKeyCommand(0, 'k', PatchUtil.C.linePrev()));
+ keysNavigation.add(new NextKeyCommand(0, 'j', PatchUtil.C.lineNext()));
+ keysNavigation.add(new PrevChunkKeyCmd(0, 'p', PatchUtil.C.chunkPrev()));
+ keysNavigation.add(new NextChunkKeyCmd(0, 'n', PatchUtil.C.chunkNext()));
+
+ keysAction.add(new OpenKeyCommand(0, 'o', PatchUtil.C.expandComment()));
+ keysAction.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, PatchUtil.C
+ .expandComment()));
+
+ if (Gerrit.isSignedIn()) {
+ keysAction.add(new InsertCommentCommand(0, 'c', PatchUtil.C
+ .commentInsert()));
+ keysAction.add(new PublishCommentsKeyCommand(0, 'r', Util.C
+ .keyPublishComments()));
+
+ // See CommentEditorPanel
+ //
+ keysComment = new KeyCommandSet(PatchUtil.C.commentEditorSet());
+ keysComment.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 's', PatchUtil.C
+ .commentSaveDraft()));
+ keysComment.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 'd', PatchUtil.C
+ .commentDiscard()));
+ keysComment.add(new NoOpKeyCommand(0, KeyCodes.KEY_ESCAPE, PatchUtil.C
+ .commentCancelEdit()));
+ } else {
+ keysComment = null;
+ }
+
+ table.setStyleName("gerrit-PatchContentTable");
+ }
+
+ void notifyDraftDelta(final int delta) {
+ if (fileList != null) {
+ fileList.notifyDraftDelta(patchKey, delta);
+ }
+ }
+
+ @Override
+ public void setRegisterKeys(final boolean on) {
+ super.setRegisterKeys(on);
+ if (on && keysComment != null && regComment == null) {
+ regComment = GlobalKey.add(this, keysComment);
+ } else if (!on && regComment != null) {
+ regComment.removeHandler();
+ regComment = null;
+ }
+ }
+
+ public void display(final Patch.Key k, final PatchSet.Id a,
+ final PatchSet.Id b, final PatchScript s) {
+ patchKey = k;
+ idSideA = a;
+ idSideB = b;
+
+ final String pathName = patchKey.get();
+ int ext = pathName.lastIndexOf('.');
+ formatLanguage = ext > 0 ? pathName.substring(ext + 1).toLowerCase() : null;
+
+ render(s);
+ }
+
+ protected abstract void render(PatchScript script);
+
+ protected abstract void onInsertComment(PatchLine pl);
+
+ public abstract void display(CommentDetail comments);
+
+ @Override
+ protected MyFlexTable createFlexTable() {
+ return new DoubleClickFlexTable();
+ }
+
+ @Override
+ protected Object getRowItemKey(final Object item) {
+ return null;
+ }
+
+ protected void initScript(final PatchScript script) {
+ if (script.getEdits().size() == 1) {
+ final SparseFileContent a = script.getA();
+ final SparseFileContent b = script.getB();
+ onlyOneHunk = a.size() == 0 || b.size() == 0;
+ } else {
+ onlyOneHunk = false;
+ }
+ }
+
+ private boolean isChunk(final int row) {
+ final Object o = getRowItem(row);
+ if (!onlyOneHunk && o instanceof PatchLine) {
+ final PatchLine pl = (PatchLine) o;
+ switch (pl.getType()) {
+ case DELETE:
+ case INSERT:
+ case REPLACE:
+ return true;
+ }
+ } else if (o instanceof CommentList) {
+ return true;
+ }
+ return false;
+ }
+
+ private int findChunkStart(int row) {
+ while (0 <= row && isChunk(row)) {
+ row--;
+ }
+ return row + 1;
+ }
+
+ private int findChunkEnd(int row) {
+ final int max = table.getRowCount();
+ while (row < max && isChunk(row)) {
+ row++;
+ }
+ return row - 1;
+ }
+
+ private static int oneBefore(final int begin) {
+ return 1 <= begin ? begin - 1 : begin;
+ }
+
+ private int oneAfter(final int end) {
+ return end + 1 < table.getRowCount() ? end + 1 : end;
+ }
+
+ private void moveToPrevChunk(int row) {
+ while (0 <= row && isChunk(row)) {
+ row--;
+ }
+ for (; 0 <= row; row--) {
+ if (isChunk(row)) {
+ final int start = findChunkStart(row);
+ movePointerTo(start, false);
+ scrollIntoView(oneBefore(start), oneAfter(row));
+ return;
+ }
+ }
+
+ // No prior hunk found? Try to hit the first line in the file.
+ //
+ for (row = 0; row < table.getRowCount(); row++) {
+ if (getRowItem(row) != null) {
+ movePointerTo(row);
+ break;
+ }
+ }
+ }
+
+ private void moveToNextChunk(int row) {
+ final int max = table.getRowCount();
+ while (row < max && isChunk(row)) {
+ row++;
+ }
+ for (; row < max; row++) {
+ if (isChunk(row)) {
+ movePointerTo(row, false);
+ scrollIntoView(oneBefore(row), oneAfter(findChunkEnd(row)));
+ return;
+ }
+ }
+
+ // No next hunk found? Try to hit the last line in the file.
+ //
+ for (row = max - 1; row >= 0; row--) {
+ if (getRowItem(row) != null) {
+ movePointerTo(row);
+ break;
+ }
+ }
+ }
+
+ /** Invoked when the user clicks on a table cell. */
+ protected abstract void onCellDoubleClick(int row, int column);
+
+ /**
+ * Invokes createCommentEditor() with an empty string as value for the comment
+ * parent UUID. This method is invoked by callers that want to create an
+ * editor for a comment that is not a reply.
+ */
+ protected void createCommentEditor(final int suggestRow, final int column,
+ final int line, final short file) {
+ createCommentEditor(suggestRow, column, line, file, null /* no parent */);
+ }
+
+ protected void createReplyEditor(final PublishedCommentPanel currentPanel) {
+ final int row = rowOf(currentPanel.getElement());
+ if (row >= 0) {
+ final int column = columnOf(currentPanel.getElement());
+ final PatchLineComment c = currentPanel.comment;
+ final String uuid = c.getKey().get();
+ final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
+ final short file;
+ if (idSideA == null) {
+ file = c.getSide();
+ } else if (idSideB.equals(psId)) {
+ file = 1;
+ } else {
+ file = 0;
+ }
+ createCommentEditor(row, column, c.getLine(), file, uuid);
+ }
+ }
+
+ private void createCommentEditor(final int suggestRow, final int column,
+ final int line, final short file, final String parentUuid) {
+ if (line < 1) {
+ // Refuse to create an editor before the start of the file.
+ //
+ return;
+ }
+
+ int row = suggestRow;
+ if (parentUuid != null) {
+ row++;
+ } else {
+ int spans[] = new int[column + 1];
+ OUTER: while (row < table.getRowCount()) {
+ int col = 0;
+ for (int cell = 0; row < table.getRowCount()
+ && cell < table.getCellCount(row); cell++) {
+ while (col < column && 0 < spans[col]) {
+ spans[col++]--;
+ }
+ spans[col] = table.getFlexCellFormatter().getRowSpan(row, cell);
+ if (col == column) {
+ final Widget w = table.getWidget(row, cell);
+ if (w instanceof CommentEditorPanel) {
+ break OUTER;
+ } else if (w instanceof CommentPanel) {
+ row++;
+ } else {
+ break OUTER;
+ }
+ }
+ }
+ }
+ }
+ if (row < table.getRowCount() && column < table.getCellCount(row)
+ && table.getWidget(row, column) instanceof CommentEditorPanel) {
+ // Don't insert two editors on the same position, it doesn't make
+ // any sense to the user.
+ //
+ ((CommentEditorPanel) table.getWidget(row, column)).setFocus(true);
+ return;
+ }
+
+ if (!Gerrit.isSignedIn()) {
+ Gerrit.doSignIn();
+ return;
+ }
+
+ final Patch.Key parentKey;
+ final short side;
+ switch (file) {
+ case 0:
+ if (idSideA == null) {
+ parentKey = new Patch.Key(idSideB, patchKey.get());
+ side = (short) 0;
+ } else {
+ parentKey = new Patch.Key(idSideA, patchKey.get());
+ side = (short) 1;
+ }
+ break;
+ case 1:
+ parentKey = new Patch.Key(idSideB, patchKey.get());
+ side = (short) 1;
+ break;
+ default:
+ throw new RuntimeException("unexpected file id " + file);
+ }
+
+ final PatchLineComment newComment =
+ new PatchLineComment(new PatchLineComment.Key(parentKey, null), line,
+ Gerrit.getUserAccount().getId(), parentUuid);
+ newComment.setSide(side);
+ newComment.setMessage("");
+
+ final CommentEditorPanel ed = new CommentEditorPanel(newComment);
+ boolean isCommentRow = false;
+ boolean needInsert = false;
+ if (row < table.getRowCount()) {
+ for (int cell = 0; cell < table.getCellCount(row); cell++) {
+ final Widget w = table.getWidget(row, cell);
+ if (w instanceof CommentEditorPanel || w instanceof CommentPanel) {
+ if (column == cell) {
+ needInsert = true;
+ }
+ isCommentRow = true;
+ }
+ }
+ }
+ if (needInsert || !isCommentRow) {
+ insertRow(row);
+ styleCommentRow(row);
+ }
+ table.setWidget(row, column, ed);
+
+ int span = 1;
+ for (int r = row + 1; r < table.getRowCount(); r++) {
+ boolean hasComment = false;
+ for (int c = 0; c < table.getCellCount(r); c++) {
+ final Widget w = table.getWidget(r, c);
+ if (w instanceof CommentPanel || w instanceof CommentEditorPanel) {
+ if (c != column) {
+ hasComment = true;
+ break;
+ }
+ }
+ }
+ if (hasComment) {
+ table.removeCell(r, column);
+ span++;
+ } else {
+ break;
+ }
+ }
+ if (span > 1) {
+ table.getFlexCellFormatter().setRowSpan(row, column, span);
+ }
+
+ for (int r = row - 1; r > 0; r--) {
+ if (getRowItem(r) instanceof CommentList) {
+ continue;
+ } else if (getRowItem(r) != null) {
+ movePointerTo(r);
+ break;
+ }
+ }
+
+ ed.setFocus(true);
+ }
+
+ protected void insertRow(final int row) {
+ table.insertRow(row);
+ table.getCellFormatter().setStyleName(row, 0, S_ICON_CELL);
+ }
+
+ @Override
+ protected void onOpenRow(final int row) {
+ final Object item = getRowItem(row);
+ if (item instanceof CommentList) {
+ for (final CommentPanel p : ((CommentList) item).panels) {
+ p.setOpen(!p.isOpen());
+ }
+ }
+ }
+
+ public void setAccountInfoCache(final AccountInfoCache aic) {
+ assert aic != null;
+ accountCache = aic;
+ }
+
+ static void destroyEditor(final FlexTable table, final int row, final int col) {
+ table.clearCell(row, col);
+ final int span = table.getFlexCellFormatter().getRowSpan(row, col);
+ boolean removeRow = true;
+ final int nCells = table.getCellCount(row);
+ for (int cell = 0; cell < nCells; cell++) {
+ if (table.getWidget(row, cell) != null) {
+ removeRow = false;
+ break;
+ }
+ }
+ if (removeRow) {
+ for (int r = row - 1; 0 <= r; r--) {
+ boolean data = false;
+ for (int c = 0; c < table.getCellCount(r); c++) {
+ data |= table.getWidget(r, c) != null;
+ final int s = table.getFlexCellFormatter().getRowSpan(r, c) - 1;
+ if (r + s == row) {
+ table.getFlexCellFormatter().setRowSpan(r, c, s);
+ }
+ }
+ if (!data) {
+ break;
+ }
+ }
+ table.removeRow(row);
+ } else if (span != 1) {
+ table.getFlexCellFormatter().setRowSpan(row, col, 1);
+ for (int r = row + 1; r < row + span; r++) {
+ table.insertCell(r, col + 1);
+ }
+ }
+ }
+
+ protected void bindComment(final int row, final int col,
+ final PatchLineComment line, final boolean isLast) {
+ if (line.getStatus() == PatchLineComment.Status.DRAFT) {
+ final CommentEditorPanel plc = new CommentEditorPanel(line);
+ table.setWidget(row, col, plc);
+
+ } else {
+ final AccountInfo author = accountCache.get(line.getAuthor());
+ final PublishedCommentPanel panel =
+ new PublishedCommentPanel(author, line);
+ table.setWidget(row, col, panel);
+
+ CommentList l = (CommentList) getRowItem(row);
+ if (l == null) {
+ l = new CommentList();
+ setRowItem(row, l);
+ }
+ l.comments.add(line);
+ l.panels.add(panel);
+ }
+
+ styleCommentRow(row);
+ }
+
+ private void styleCommentRow(final int row) {
+ final CellFormatter fmt = table.getCellFormatter();
+ final Element iconCell = fmt.getElement(row, 0);
+ UIObject.setStyleName(DOM.getParent(iconCell), "CommentHolder", true);
+ }
+
+ protected static class CommentList {
+ final List<PatchLineComment> comments = new ArrayList<PatchLineComment>();
+ final List<PublishedCommentPanel> panels =
+ new ArrayList<PublishedCommentPanel>();
+ }
+
+ protected class DoubleClickFlexTable extends MyFlexTable {
+ public DoubleClickFlexTable() {
+ sinkEvents(Event.ONDBLCLICK | Event.ONCLICK);
+ }
+
+ @Override
+ public void onBrowserEvent(final Event event) {
+ switch (DOM.eventGetType(event)) {
+ case Event.ONCLICK: {
+ // Find out which cell was actually clicked.
+ final Element td = getEventTargetCell(event);
+ if (td == null) {
+ break;
+ }
+ final int row = rowOf(td);
+ if (getRowItem(row) != null) {
+ movePointerTo(row);
+ return;
+ }
+ break;
+ }
+ case Event.ONDBLCLICK: {
+ // Find out which cell was actually clicked.
+ Element td = getEventTargetCell(event);
+ if (td == null) {
+ return;
+ }
+ onCellDoubleClick(rowOf(td), columnOf(td));
+ return;
+ }
+ }
+ super.onBrowserEvent(event);
+ }
+ }
+
+ public static class NoOpKeyCommand extends NeedsSignInKeyCommand {
+ public NoOpKeyCommand(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ }
+ }
+
+ public class InsertCommentCommand extends NeedsSignInKeyCommand {
+ public InsertCommentCommand(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ ensurePointerVisible();
+ for (int row = getCurrentRow(); 0 <= row; row--) {
+ final Object item = getRowItem(row);
+ if (item instanceof PatchLine) {
+ onInsertComment((PatchLine) item);
+ return;
+ } else if (item instanceof CommentList) {
+ continue;
+ } else {
+ return;
+ }
+ }
+ }
+ }
+
+ public class PublishCommentsKeyCommand extends NeedsSignInKeyCommand {
+ public PublishCommentsKeyCommand(int mask, char key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ final PatchSet.Id id = patchKey.getParentKey();
+ Gerrit.display("change,publish," + id.toString(),
+ new PublishCommentScreen(id));
+ }
+ }
+
+ public class PrevChunkKeyCmd extends KeyCommand {
+ public PrevChunkKeyCmd(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ ensurePointerVisible();
+ moveToPrevChunk(getCurrentRow());
+ }
+ }
+
+ public class NextChunkKeyCmd extends KeyCommand {
+ public NextChunkKeyCmd(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ ensurePointerVisible();
+ moveToNextChunk(getCurrentRow());
+ }
+ }
+
+ private class PublishedCommentPanel extends CommentPanel implements
+ ClickHandler {
+ final PatchLineComment comment;
+
+ PublishedCommentPanel(final AccountInfo author, final PatchLineComment c) {
+ super(author, c.getWrittenOn(), c.getMessage());
+ this.comment = c;
+
+ final Button reply = new Button(PatchUtil.C.buttonReply());
+ reply.addClickHandler(this);
+ getButtonPanel().add(reply);
+ }
+
+ @Override
+ public void onClick(final ClickEvent event) {
+ createReplyEditor(this);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
new file mode 100644
index 0000000000..92b895fbff
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
@@ -0,0 +1,338 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.CommentPanel;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.Focusable;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.globalkey.client.NpTextArea;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+import java.sql.Timestamp;
+
+public class CommentEditorPanel extends CommentPanel implements ClickHandler,
+ DoubleClickHandler {
+ private static final int INITIAL_COLS = 60;
+ private static final int INITIAL_LINES = 5;
+ private static final int MAX_LINES = 30;
+ private static final AsyncCallback<VoidResult> NULL_CALLBACK =
+ new AsyncCallback<VoidResult>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ }
+
+ @Override
+ public void onSuccess(VoidResult result) {
+ }
+ };
+
+ private PatchLineComment comment;
+
+ private final NpTextArea text;
+ private final Button edit;
+ private final Button save;
+ private final Button cancel;
+ private final Button discard;
+ private final Timer expandTimer;
+
+ public CommentEditorPanel(final PatchLineComment plc) {
+ comment = plc;
+
+ addStyleName("gerrit-CommentEditorPanel");
+ setAuthorNameText(PatchUtil.C.draft());
+ setMessageText(plc.getMessage());
+ addDoubleClickHandler(this);
+
+ expandTimer = new Timer() {
+ @Override
+ public void run() {
+ expandText();
+ }
+ };
+ text = new NpTextArea();
+ text.setText(comment.getMessage());
+ text.setCharacterWidth(INITIAL_COLS);
+ text.setVisibleLines(INITIAL_LINES);
+ DOM.setElementPropertyBoolean(text.getElement(), "spellcheck", true);
+ text.addKeyPressHandler(new KeyPressHandler() {
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (event.getCharCode() == KeyCodes.KEY_ESCAPE
+ && !event.isAnyModifierKeyDown()) {
+ event.preventDefault();
+ if (isNew()) {
+ onDiscard();
+ } else {
+ render();
+ }
+ return;
+ }
+
+ if ((event.isControlKeyDown() || event.isMetaKeyDown())
+ && !event.isAltKeyDown() && !event.isShiftKeyDown()) {
+ switch (event.getCharCode()) {
+ case 's':
+ event.preventDefault();
+ onSave(NULL_CALLBACK);
+ return;
+
+ case 'd':
+ event.preventDefault();
+ if (isNew()) {
+ onDiscard();
+ } else if (Window.confirm(PatchUtil.C.confirmDiscard())) {
+ onDiscard();
+ } else {
+ text.setFocus(true);
+ }
+ return;
+ }
+ }
+
+ expandTimer.schedule(250);
+ }
+ });
+ addContent(text);
+
+ edit = new Button();
+ edit.setText(PatchUtil.C.buttonEdit());
+ edit.addClickHandler(this);
+ getButtonPanel().add(edit);
+
+ save = new Button();
+ save.setText(PatchUtil.C.buttonSave());
+ save.addClickHandler(this);
+ getButtonPanel().add(save);
+
+ cancel = new Button();
+ cancel.setText(PatchUtil.C.buttonCancel());
+ cancel.addClickHandler(this);
+ getButtonPanel().add(cancel);
+
+ discard = new Button();
+ discard.setText(PatchUtil.C.buttonDiscard());
+ discard.addClickHandler(this);
+ getButtonPanel().add(discard);
+
+ setOpen(true);
+ if (isNew()) {
+ edit();
+ } else {
+ render();
+ }
+ }
+
+ private void expandText() {
+ final double cols = text.getCharacterWidth();
+ int rows = 2;
+ for (final String line : text.getText().split("\n")) {
+ rows += Math.ceil((1.0 + line.length()) / cols);
+ }
+ rows = Math.max(INITIAL_LINES, Math.min(rows, MAX_LINES));
+ if (text.getVisibleLines() != rows) {
+ text.setVisibleLines(rows);
+ }
+ }
+
+ private void edit() {
+ if (!isOpen()) {
+ setOpen(true);
+ }
+ text.setText(comment.getMessage());
+ expandText();
+ stateEdit(true);
+ text.setFocus(true);
+ }
+
+ private void render() {
+ final Timestamp on = comment.getWrittenOn();
+ setDateText(PatchUtil.M.draftSaved(new java.util.Date(on.getTime())));
+ setMessageText(comment.getMessage());
+ stateEdit(false);
+ }
+
+ private void stateEdit(final boolean inEdit) {
+ expandTimer.cancel();
+ setMessageTextVisible(!inEdit);
+ edit.setVisible(!inEdit);
+
+ text.setVisible(inEdit);
+ save.setVisible(inEdit);
+ cancel.setVisible(inEdit && !isNew());
+ discard.setVisible(inEdit);
+ }
+
+ void setFocus(final boolean take) {
+ if (take && !isOpen()) {
+ setOpen(true);
+ }
+ if (text.isVisible()) {
+ text.setFocus(take);
+ } else if (take) {
+ edit();
+ }
+ }
+
+ boolean isNew() {
+ return comment.getKey().get() == null;
+ }
+
+ @Override
+ public void onDoubleClick(final DoubleClickEvent event) {
+ edit();
+ }
+
+ @Override
+ public void onClick(final ClickEvent event) {
+ final Widget sender = (Widget) event.getSource();
+ if (sender == edit) {
+ edit();
+
+ } else if (sender == save) {
+ onSave(NULL_CALLBACK);
+
+ } else if (sender == cancel) {
+ render();
+
+ } else if (sender == discard) {
+ onDiscard();
+ }
+ }
+
+ public void saveDraft(AsyncCallback<VoidResult> onSave) {
+ if (isOpen() && text.isVisible()) {
+ onSave(onSave);
+ } else {
+ onSave.onSuccess(VoidResult.INSTANCE);
+ }
+ }
+
+ private void onSave(final AsyncCallback<VoidResult> onSave) {
+ expandTimer.cancel();
+ final String txt = text.getText().trim();
+ if ("".equals(txt)) {
+ return;
+ }
+
+ comment.setMessage(txt);
+ text.setReadOnly(true);
+ save.setEnabled(false);
+ cancel.setEnabled(false);
+ discard.setEnabled(false);
+
+ PatchUtil.DETAIL_SVC.saveDraft(comment,
+ new GerritCallback<PatchLineComment>() {
+ public void onSuccess(final PatchLineComment result) {
+ if (isNew()) {
+ notifyDraftDelta(1);
+ }
+ comment = result;
+ text.setReadOnly(false);
+ save.setEnabled(true);
+ cancel.setEnabled(true);
+ discard.setEnabled(true);
+ render();
+ onSave.onSuccess(VoidResult.INSTANCE);
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ text.setReadOnly(false);
+ save.setEnabled(true);
+ cancel.setEnabled(true);
+ discard.setEnabled(true);
+ super.onFailure(caught);
+ onSave.onFailure(caught);
+ }
+ });
+ }
+
+ private void notifyDraftDelta(final int delta) {
+ Widget p = getParent();
+ if (p != null) {
+ p = p.getParent();
+ if (p != null) {
+ ((AbstractPatchContentTable) p).notifyDraftDelta(delta);
+ }
+ }
+ }
+
+ private void onDiscard() {
+ expandTimer.cancel();
+ if (isNew()) {
+ removeUI();
+ return;
+ }
+
+ text.setReadOnly(true);
+ save.setEnabled(false);
+ cancel.setEnabled(false);
+ discard.setEnabled(false);
+
+ PatchUtil.DETAIL_SVC.deleteDraft(comment.getKey(),
+ new GerritCallback<VoidResult>() {
+ public void onSuccess(final VoidResult result) {
+ notifyDraftDelta(-1);
+ removeUI();
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ text.setReadOnly(false);
+ save.setEnabled(true);
+ cancel.setEnabled(true);
+ discard.setEnabled(true);
+ super.onFailure(caught);
+ }
+ });
+ }
+
+ private void removeUI() {
+ final FlexTable table = (FlexTable) getParent();
+ final int nRows = table.getRowCount();
+ for (int row = 0; row < nRows; row++) {
+ final int nCells = table.getCellCount(row);
+ for (int cell = 0; cell < nCells; cell++) {
+ if (table.getWidget(row, cell) == this) {
+ AbstractPatchContentTable.destroyEditor(table, row, cell);
+ Widget p = table;
+ while (p != null) {
+ if (p instanceof Focusable) {
+ ((Focusable) p).setFocus(true);
+ break;
+ }
+ p = p.getParent();
+ }
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
new file mode 100644
index 0000000000..bd7336709e
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
@@ -0,0 +1,211 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.ui.FancyFlexTable;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.RadioButton;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A table used to specify which two patch sets should be diff'ed.
+ */
+class HistoryTable extends FancyFlexTable<Patch> {
+ private final PatchScreen screen;
+ final List<HistoryRadio> all = new ArrayList<HistoryRadio>();
+
+ HistoryTable(final PatchScreen parent) {
+ setStyleName("gerrit-PatchHistoryTable");
+ screen = parent;
+ table.addStyleName("gerrit-ChangeTable");
+ }
+
+ void onClick(final HistoryRadio b) {
+ switch (b.file) {
+ case 0:
+ screen.setSideA(b.patchSetId);
+ break;
+ case 1:
+ screen.setSideB(b.patchSetId);
+ break;
+ default:
+ return;
+ }
+
+ enableAll(false);
+ screen.refresh(false);
+ }
+
+ void enableAll(final boolean on) {
+ for (final HistoryRadio a : all) {
+ a.setEnabled(on);
+ }
+ }
+
+ void display(final List<Patch> result) {
+ all.clear();
+
+ final SafeHtmlBuilder nc = new SafeHtmlBuilder();
+ appendHeader(nc);
+ appendRow(nc, null);
+ for (final Patch k : result) {
+ appendRow(nc, k);
+ }
+ resetHtml(nc);
+
+ int row = 1;
+ {
+ final Patch k = new Patch(new Patch.Key(null, ""));
+ setRowItem(row, k);
+ installRadio(row, k, 0, screen.idSideA);
+ row++;
+ }
+ for (final Patch k : result) {
+ setRowItem(row, k);
+ installRadio(row, k, 0, screen.idSideA);
+ installRadio(row, k, 1, screen.idSideB);
+ row++;
+ }
+ }
+
+ private void installRadio(final int row, final Patch k, final int file,
+ final PatchSet.Id cur) {
+ final PatchSet.Id psid = k.getKey().getParentKey();
+ final HistoryRadio b = new HistoryRadio(psid, file);
+ b.setValue(eq(cur, psid));
+
+ final int cell = radioCell(file);
+ table.setWidget(row, cell, b);
+ table.getCellFormatter().setHorizontalAlignment(row, cell,
+ HasHorizontalAlignment.ALIGN_CENTER);
+ all.add(b);
+ }
+
+ private boolean eq(final PatchSet.Id cur, final PatchSet.Id psid) {
+ if (cur == null && psid == null) {
+ return true;
+ }
+ return psid != null && psid.equals(cur);
+ }
+
+ private int radioCell(final int file) {
+ return 2 + file;
+ }
+
+ private void appendHeader(final SafeHtmlBuilder m) {
+ m.openTr();
+
+ m.openTd();
+ m.addStyleName(S_ICON_HEADER);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(PatchUtil.C.patchHeaderOld());
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(PatchUtil.C.patchHeaderNew());
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_HEADER);
+ m.append(Util.C.patchTableColumnComments());
+ m.closeTd();
+
+ m.closeTr();
+ }
+
+ private void appendRow(final SafeHtmlBuilder m, final Patch k) {
+ m.openTr();
+
+ m.openTd();
+ m.addStyleName(S_ICON_CELL);
+ m.addStyleName("LeftMostCell");
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ m.setAttribute("align", "right");
+ if (k != null) {
+ final PatchSet.Id psId = k.getKey().getParentKey();
+ m.append(Util.M.patchSetHeader(psId.get()));
+ } else {
+ m.append("Base");
+ }
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ m.nbsp();
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName(S_DATA_CELL);
+ if (k != null && k.getCommentCount() > 0) {
+ m.append(Util.M.patchTableComments(k.getCommentCount()));
+ } else {
+ m.nbsp();
+ }
+ m.closeTd();
+
+ m.closeTr();
+ }
+
+ private class HistoryRadio extends RadioButton {
+ final PatchSet.Id patchSetId;
+ final int file;
+
+ HistoryRadio(final PatchSet.Id ps, final int f) {
+ super(String.valueOf(f));
+ sinkEvents(Event.ONCLICK);
+ patchSetId = ps;
+ file = f;
+ }
+
+ @Override
+ public void onBrowserEvent(final Event event) {
+ switch (DOM.eventGetType(event)) {
+ case Event.ONCLICK:
+ onClick(this);
+ break;
+ default:
+ super.onBrowserEvent(event);
+ }
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java
new file mode 100644
index 0000000000..bc7922a690
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import com.google.gerrit.client.changes.PatchTable;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gwt.event.logical.shared.ResizeEvent;
+import com.google.gwt.event.logical.shared.ResizeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.ScrollPanel;
+import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.HidePopupPanelCommand;
+import com.google.gwtexpui.user.client.PluginSafeDialogBox;
+
+class PatchBrowserPopup extends PluginSafeDialogBox implements
+ PositionCallback, ResizeHandler {
+ private final Patch.Key callerKey;
+ private final PatchTable fileList;
+ private final ScrollPanel sp;
+ private HandlerRegistration regWindowResize;
+
+ PatchBrowserPopup(final Patch.Key pk, final PatchTable fl) {
+ super(true/* autohide */, false/* modal */);
+
+ callerKey = pk;
+ fileList = fl;
+ sp = new ScrollPanel(fileList);
+
+ final FlowPanel body = new FlowPanel();
+ body.setStyleName("gerrit-PatchBrowserPopup-Body");
+ body.add(sp);
+
+ setText(Util.M.patchSetHeader(callerKey.getParentKey().get()));
+ setWidget(body);
+ addStyleName("gerrit-PatchBrowserPopup");
+ }
+
+ @Override
+ public void setPosition(final int myWidth, int myHeight) {
+ final int dLeft = (Window.getClientWidth() - myWidth) >> 1;
+ final int cHeight = Window.getClientHeight();
+ final int cHeight2 = 2 * cHeight / 3;
+ final int sLeft = Window.getScrollLeft();
+ final int sTop = Window.getScrollTop();
+
+ if (myHeight > cHeight2) {
+ sp.setHeight((cHeight2 - 50) + "px");
+ myHeight = getOffsetHeight();
+ }
+ setPopupPosition(sLeft + dLeft, (sTop + cHeight) - (myHeight + 10));
+ }
+
+ @Override
+ public void onResize(final ResizeEvent event) {
+ sp.setWidth((Window.getClientWidth() - 60) + "px");
+ setPosition(getOffsetWidth(), getOffsetHeight());
+ }
+
+ @Override
+ public void hide() {
+ if (regWindowResize != null) {
+ regWindowResize.removeHandler();
+ regWindowResize = null;
+ }
+ super.hide();
+ }
+
+ @Override
+ public void show() {
+ super.show();
+ if (regWindowResize == null) {
+ regWindowResize = Window.addResizeHandler(this);
+ }
+
+ GlobalKey.dialog(this);
+ GlobalKey.addApplication(this, new HidePopupPanelCommand(0, 'f', this));
+
+ if (!fileList.isLoaded()) {
+ fileList.onTableLoaded(new Command() {
+ @Override
+ public void execute() {
+ sp.setHeight("");
+ setPosition(getOffsetWidth(), getOffsetHeight());
+ fileList.setRegisterKeys(true);
+ fileList.movePointerTo(callerKey);
+ }
+ });
+ }
+ }
+
+ public void open() {
+ if (!fileList.isLoaded()) {
+ sp.setHeight("22px");
+ }
+ sp.setWidth((Window.getClientWidth() - 60) + "px");
+ setPopupPositionAndShow(this);
+ if (fileList.isLoaded()) {
+ fileList.setRegisterKeys(true);
+ fileList.movePointerTo(callerKey);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
index 652776d76c..652776d76c 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
index a4ff9304d6..a4ff9304d6 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchLine.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java
index 9726bbb7dc..9726bbb7dc 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchLine.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchLine.java
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java
index d48c2f85fc..d48c2f85fc 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchMessages.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.java
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties
index 961b0e5e9d..961b0e5e9d 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages.properties
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties
index 9fceabe709..9fceabe709 100644
--- a/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchMessages_en.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
new file mode 100644
index 0000000000..cb65a9386f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
@@ -0,0 +1,556 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.DEFAULT_CONTEXT;
+import static com.google.gerrit.reviewdb.AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.changes.ChangeScreen;
+import com.google.gerrit.client.changes.PatchTable;
+import com.google.gerrit.client.changes.Util;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.ChangeLink;
+import com.google.gerrit.client.ui.DirectScreenLink;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchScriptSettings;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.event.logical.shared.OpenEvent;
+import com.google.gwt.event.logical.shared.OpenHandler;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.DisclosurePanel;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.globalkey.client.GlobalKey;
+import com.google.gwtexpui.globalkey.client.KeyCommand;
+import com.google.gwtexpui.globalkey.client.KeyCommandSet;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtjsonrpc.client.VoidResult;
+
+public abstract class PatchScreen extends Screen {
+ public static class SideBySide extends PatchScreen {
+ public SideBySide(final Patch.Key id, final int patchIndex,
+ final PatchTable patchTable) {
+ super(id, patchIndex, patchTable);
+ }
+
+ @Override
+ protected SideBySideTable createContentTable() {
+ return new SideBySideTable();
+ }
+
+ @Override
+ protected PatchScreen.Type getPatchScreenType() {
+ return PatchScreen.Type.SIDE_BY_SIDE;
+ }
+ }
+
+ public static class Unified extends PatchScreen {
+ public Unified(final Patch.Key id, final int patchIndex,
+ final PatchTable patchTable) {
+ super(id, patchIndex, patchTable);
+ }
+
+ @Override
+ protected UnifiedDiffTable createContentTable() {
+ return new UnifiedDiffTable();
+ }
+
+ @Override
+ protected PatchScreen.Type getPatchScreenType() {
+ return PatchScreen.Type.UNIFIED;
+ }
+ }
+
+ // Which patch set id's are being diff'ed
+ private static PatchSet.Id diffSideA = null;
+ private static PatchSet.Id diffSideB = null;
+ private static Boolean historyOpen = null;
+ private static final OpenHandler<DisclosurePanel> cacheOpenState =
+ new OpenHandler<DisclosurePanel>() {
+ @Override
+ public void onOpen(OpenEvent<DisclosurePanel> event) {
+ historyOpen = true;
+ }
+ };
+ private static final CloseHandler<DisclosurePanel> cacheCloseState =
+ new CloseHandler<DisclosurePanel>() {
+ @Override
+ public void onClose(CloseEvent<DisclosurePanel> event) {
+ historyOpen = false;
+ }
+ };
+
+ // The change id for which the above patch set id's are valid
+ private static Change.Id currentChangeId = null;
+
+ protected final Patch.Key patchKey;
+ protected PatchTable fileList;
+ protected PatchSet.Id idSideA;
+ protected PatchSet.Id idSideB;
+ protected final PatchScriptSettings scriptSettings;
+
+ private DisclosurePanel historyPanel;
+ private HistoryTable historyTable;
+ private FlowPanel contentPanel;
+ private Label noDifference;
+ private AbstractPatchContentTable contentTable;
+
+ private int rpcSequence;
+ private PatchScript script;
+ private CommentDetail comments;
+
+ /** The index of the file we are currently looking at among the fileList */
+ private int patchIndex;
+
+ /** Keys that cause an action on this screen */
+ private KeyCommandSet keysNavigation;
+ private HandlerRegistration regNavigation;
+
+ /** Link to the screen for the previous file, null if not applicable */
+ private DirectScreenLink previousFileLink;
+
+ /** Link to the screen for the next file, null if not applicable */
+ private DirectScreenLink nextFileLink;
+
+ private static final char SHORTCUT_PREVIOUS_FILE = '[';
+ private static final char SHORTCUT_NEXT_FILE = ']';
+
+ /**
+ * How this patch should be displayed in the patch screen.
+ */
+ public static enum Type {
+ UNIFIED, SIDE_BY_SIDE
+ }
+
+ protected PatchScreen(final Patch.Key id, final int patchIndex,
+ final PatchTable patchTable) {
+ patchKey = id;
+ fileList = patchTable;
+
+ // If we have any diff side stored, make sure they are applicable to the
+ // current change, discard them otherwise.
+ //
+ Change.Id thisChangeId = id.getParentKey().getParentKey();
+ if (currentChangeId != null && !currentChangeId.equals(thisChangeId)) {
+ diffSideA = null;
+ diffSideB = null;
+ historyOpen = null;
+ }
+ currentChangeId = thisChangeId;
+ idSideA = diffSideA; // null here means we're diff'ing from the Base
+ idSideB = diffSideB != null ? diffSideB : id.getParentKey();
+ this.patchIndex = patchIndex;
+ scriptSettings = new PatchScriptSettings();
+
+ initContextLines();
+ }
+
+ /**
+ * Initialize the context lines to the user's preference, or to the default
+ * number if the user is not logged in.
+ */
+ private void initContextLines() {
+ if (Gerrit.isSignedIn()) {
+ final AccountGeneralPreferences p =
+ Gerrit.getUserAccount().getGeneralPreferences();
+ scriptSettings.setContext(p.getDefaultContext());
+ } else {
+ scriptSettings.setContext(DEFAULT_CONTEXT);
+ }
+ }
+
+ @Override
+ protected void onInitUI() {
+ super.onInitUI();
+
+ keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
+ keysNavigation.add(new UpToChangeCommand(0, 'u', PatchUtil.C.upToChange()));
+ keysNavigation.add(new FileListCmd(0, 'f', PatchUtil.C.fileList()));
+
+ historyTable = new HistoryTable(this);
+ historyPanel = new DisclosurePanel(PatchUtil.C.patchHistoryTitle());
+ historyPanel.setContent(historyTable);
+ historyPanel.setVisible(false);
+ // If the user selected a different patch set than the default for either
+ // side, expand the history panel
+ historyPanel.setOpen(diffSideA != null || diffSideB != null
+ || (historyOpen != null && historyOpen));
+ historyPanel.addOpenHandler(cacheOpenState);
+ historyPanel.addCloseHandler(cacheCloseState);
+ add(historyPanel);
+ initDisplayControls();
+
+ noDifference = new Label(PatchUtil.C.noDifference());
+ noDifference.setStyleName("gerrit-PatchNoDifference");
+ noDifference.setVisible(false);
+
+ contentTable = createContentTable();
+ contentTable.fileList = fileList;
+
+ add(createNextPrevLinks());
+ contentPanel = new FlowPanel();
+ contentPanel.setStyleName("gerrit-SideBySideScreen-SideBySideTable");
+ contentPanel.add(noDifference);
+ contentPanel.add(contentTable);
+ add(contentPanel);
+ add(createNextPrevLinks());
+
+ // This must be done after calling createNextPrevLinks(), which initializes
+ // these fields
+ if (previousFileLink != null) {
+ installLinkShortCut(previousFileLink, SHORTCUT_PREVIOUS_FILE, PatchUtil.C
+ .previousFileHelp());
+ }
+ if (nextFileLink != null) {
+ installLinkShortCut(nextFileLink, SHORTCUT_NEXT_FILE, PatchUtil.C
+ .nextFileHelp());
+ }
+ }
+
+ private void installLinkShortCut(final DirectScreenLink link, char shortcut,
+ String help) {
+ keysNavigation.add(new KeyCommand(0, shortcut, help) {
+ @Override
+ public void onKeyPress(KeyPressEvent event) {
+ link.go();
+ }
+ });
+ }
+
+ private void initDisplayControls() {
+ final Grid displayControls = new Grid(0, 5);
+ displayControls.setStyleName("gerrit-PatchScreen-DisplayControls");
+ add(displayControls);
+
+ createIgnoreWhitespace(displayControls, 0, 0);
+ createContext(displayControls, 0, 2);
+ }
+
+ /**
+ * Add the contextual widgets for this patch: "Show full files" and
+ * "Keep unreviewed"
+ */
+ private void createContext(final Grid parent, final int row, final int col) {
+ parent.resizeRows(row + 1);
+
+ // Show full files
+ final CheckBox cb = new CheckBox(PatchUtil.C.showFullFiles());
+ cb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<Boolean> event) {
+ if (event.getValue()) {
+ // Show a diff of the full files
+ scriptSettings.setContext(WHOLE_FILE_CONTEXT);
+ } else {
+ // Restore the context lines to the user's preference
+ initContextLines();
+ }
+ refresh(false /* not the first time */);
+ }
+ });
+ parent.setWidget(row, col + 1, cb);
+
+ // "Reviewed" check box
+ if (Gerrit.isSignedIn()) {
+ final CheckBox ku = new CheckBox(PatchUtil.C.reviewed());
+ ku.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
+ @Override
+ public void onValueChange(ValueChangeEvent<Boolean> event) {
+ setReviewedByCurrentUser(event.getValue());
+ }
+ });
+ // Checked by default
+ ku.setValue(true);
+ parent.setWidget(row, col + 2, ku);
+ }
+
+ }
+
+ private void setReviewedByCurrentUser(boolean reviewed) {
+ if (fileList != null) {
+ fileList.updateReviewedStatus(patchKey, reviewed);
+ }
+
+ PatchUtil.DETAIL_SVC.setReviewedByCurrentUser(patchKey, reviewed,
+ new AsyncCallback<VoidResult>() {
+
+ @Override
+ public void onFailure(Throwable arg0) {
+ // nop
+ }
+
+ @Override
+ public void onSuccess(VoidResult result) {
+ // nop
+ }
+
+ });
+ }
+
+ private void createIgnoreWhitespace(final Grid parent, final int row,
+ final int col) {
+ parent.resizeRows(row + 1);
+ final ListBox ws = new ListBox();
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_NONE(), Whitespace.IGNORE_NONE
+ .name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_AT_EOL(),
+ Whitespace.IGNORE_SPACE_AT_EOL.name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_CHANGE(),
+ Whitespace.IGNORE_SPACE_CHANGE.name());
+ ws.addItem(PatchUtil.C.whitespaceIGNORE_ALL_SPACE(),
+ Whitespace.IGNORE_ALL_SPACE.name());
+ ws.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(ChangeEvent event) {
+ final int sel = ws.getSelectedIndex();
+ if (0 <= sel) {
+ scriptSettings.setWhitespace(Whitespace.valueOf(ws.getValue(sel)));
+ refresh(false /* not the first time */);
+ }
+ }
+ });
+ parent.setText(row, col, PatchUtil.C.whitespaceIgnoreLabel());
+ parent.setWidget(row, col + 1, ws);
+ }
+
+ private Widget createNextPrevLinks() {
+ final Grid table = new Grid(1, 3);
+ final CellFormatter fmt = table.getCellFormatter();
+ table.setStyleName("gerrit-SideBySideScreen-LinkTable");
+ fmt.setHorizontalAlignment(0, 0, HasHorizontalAlignment.ALIGN_LEFT);
+ fmt.setHorizontalAlignment(0, 1, HasHorizontalAlignment.ALIGN_CENTER);
+ fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
+
+ if (fileList != null) {
+ previousFileLink =
+ fileList.getPreviousPatchLink(patchIndex, getPatchScreenType());
+ table.setWidget(0, 0, previousFileLink);
+
+ nextFileLink =
+ fileList.getNextPatchLink(patchIndex, getPatchScreenType());
+ table.setWidget(0, 2, nextFileLink);
+ }
+
+ final ChangeLink up =
+ new ChangeLink("", patchKey.getParentKey().getParentKey());
+ SafeHtml.set(up, SafeHtml.asis(Util.C.upToChangeIconLink()));
+ table.setWidget(0, 1, up);
+
+ return table;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ refresh(true);
+ }
+
+ @Override
+ protected void onUnload() {
+ if (regNavigation != null) {
+ regNavigation.removeHandler();
+ regNavigation = null;
+ }
+ super.onUnload();
+ }
+
+ @Override
+ public void registerKeys() {
+ super.registerKeys();
+ contentTable.setRegisterKeys(contentTable.isVisible());
+ regNavigation = GlobalKey.add(this, keysNavigation);
+ }
+
+ protected abstract AbstractPatchContentTable createContentTable();
+
+ protected abstract PatchScreen.Type getPatchScreenType();
+
+ protected void refresh(final boolean isFirst) {
+ final int rpcseq = ++rpcSequence;
+ script = null;
+ comments = null;
+
+ // Mark this file reviewed as soon we display the diff screen
+ if (Gerrit.isSignedIn() && isFirst) {
+ setReviewedByCurrentUser(true /* reviewed */);
+ }
+
+ PatchUtil.DETAIL_SVC.patchScript(patchKey, idSideA, idSideB,
+ scriptSettings, new GerritCallback<PatchScript>() {
+ public void onSuccess(final PatchScript result) {
+ if (rpcSequence == rpcseq) {
+ script = result;
+ onResult();
+ }
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ if (rpcSequence == rpcseq) {
+ super.onFailure(caught);
+ }
+ }
+ });
+
+ PatchUtil.DETAIL_SVC.patchComments(patchKey, idSideA, idSideB,
+ new GerritCallback<CommentDetail>() {
+ public void onSuccess(final CommentDetail result) {
+ if (rpcSequence == rpcseq) {
+ comments = result;
+ onResult();
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ // Ignore no such entity, the patch script RPC above would
+ // also notice the problem and report it.
+ //
+ if (!isNoSuchEntity(caught) && rpcSequence == rpcseq) {
+ super.onFailure(caught);
+ }
+ }
+ });
+ }
+
+ private void onResult() {
+ if (script != null && comments != null) {
+ final Change.Key cid = script.getChangeId();
+ final String path = patchKey.get();
+ String fileName = path;
+ final int last = fileName.lastIndexOf('/');
+ if (last >= 0) {
+ fileName = fileName.substring(last + 1);
+ }
+
+ setWindowTitle(PatchUtil.M.patchWindowTitle(cid.abbreviate(), fileName));
+ setPageTitle(PatchUtil.M.patchPageTitle(cid.abbreviate(), path));
+
+ historyTable.display(comments.getHistory());
+ historyPanel.setVisible(true);
+
+ // True if there are differences between the two patch sets
+ boolean hasEdits = !script.getEdits().isEmpty();
+ // True if this change is a mode change or a pure rename/copy
+ boolean hasMeta = !script.getPatchHeader().isEmpty();
+
+ boolean hasDifferences = hasEdits || hasMeta;
+ boolean pureMetaChange = !hasEdits && hasMeta;
+
+ if (contentTable instanceof SideBySideTable && pureMetaChange) {
+ // User asked for SideBySide (or a link guessed, wrong) and we can't
+ // show a binary or pure-rename change there accurately. Switch to
+ // the unified view instead.
+ //
+ contentTable.removeFromParent();
+ contentTable = new UnifiedDiffTable();
+ contentTable.fileList = fileList;
+ contentPanel.add(contentTable);
+ History.newItem(HistoryHandler.toPatchUnified(patchKey), false);
+ }
+
+ if (hasDifferences) {
+ contentTable.display(patchKey, idSideA, idSideB, script);
+ contentTable.display(comments);
+ contentTable.finishDisplay();
+ }
+ showPatch(hasDifferences);
+
+ script = null;
+ comments = null;
+
+ if (!isCurrentView()) {
+ display();
+ }
+ }
+ }
+
+ private void showPatch(final boolean showPatch) {
+ noDifference.setVisible(!showPatch);
+ contentTable.setVisible(showPatch);
+ contentTable.setRegisterKeys(isCurrentView() && showPatch);
+ }
+
+ public void setSideA(PatchSet.Id patchSetId) {
+ idSideA = patchSetId;
+ diffSideA = patchSetId;
+ }
+
+ public void setSideB(PatchSet.Id patchSetId) {
+ idSideB = patchSetId;
+ diffSideB = patchSetId;
+ }
+
+ public class UpToChangeCommand extends KeyCommand {
+ public UpToChangeCommand(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ final Change.Id ck = patchKey.getParentKey().getParentKey();
+ Gerrit.display(PageLinks.toChange(ck), new ChangeScreen(ck));
+ }
+ }
+
+ public class FileListCmd extends KeyCommand {
+ public FileListCmd(int mask, int key, String help) {
+ super(mask, key, help);
+ }
+
+ @Override
+ public void onKeyPress(final KeyPressEvent event) {
+ if (fileList == null || fileList.isAttached()) {
+ final PatchSet.Id psid = patchKey.getParentKey();
+ fileList = new PatchTable();
+ fileList.setSavePointerId("PatchTable " + psid);
+ Util.DETAIL_SVC.patchSetDetail(psid,
+ new GerritCallback<PatchSetDetail>() {
+ public void onSuccess(final PatchSetDetail result) {
+ fileList.display(psid, result.getPatches());
+ }
+ });
+ }
+
+ final PatchBrowserPopup p = new PatchBrowserPopup(patchKey, fileList);
+ p.open();
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
new file mode 100644
index 0000000000..709685f795
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import com.google.gerrit.common.data.PatchDetailService;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class PatchUtil {
+ public static final PatchConstants C = GWT.create(PatchConstants.class);
+ public static final PatchMessages M = GWT.create(PatchMessages.class);
+ public static final PatchDetailService DETAIL_SVC;
+
+ static {
+ DETAIL_SVC = GWT.create(PatchDetailService.class);
+ JsonUtil.bind(DETAIL_SVC, "rpc/PatchDetailService");
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
new file mode 100644
index 0000000000..3c641ff8a3
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
@@ -0,0 +1,290 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import static com.google.gerrit.client.patches.PatchLine.Type.CONTEXT;
+import static com.google.gerrit.client.patches.PatchLine.Type.DELETE;
+import static com.google.gerrit.client.patches.PatchLine.Type.INSERT;
+import static com.google.gerrit.client.patches.PatchLine.Type.REPLACE;
+
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.EditList;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.SparseFileContent;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.safehtml.client.PrettyFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class SideBySideTable extends AbstractPatchContentTable {
+ private static final int COL_A = 2;
+ private static final int COL_B = 4;
+
+ @Override
+ protected void onCellDoubleClick(final int row, int column) {
+ if (column > 0 && getRowItem(row) instanceof PatchLine) {
+ final PatchLine line = (PatchLine) getRowItem(row);
+ final short file = (short) ((column - 1) / 2);
+ if (column < (1 + file * 2 + 1)) {
+ column++;
+ }
+ switch (file) {
+ case 0:
+ createCommentEditor(row + 1, column, line.getLineA(), file);
+ break;
+ case 1:
+ createCommentEditor(row + 1, column, line.getLineB(), file);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onInsertComment(final PatchLine line) {
+ final int row = getCurrentRow();
+ createCommentEditor(row + 1, 4, line.getLineB(), (short) 1);
+ }
+
+ @Override
+ protected void render(final PatchScript script) {
+ final SparseFileContent a = script.getA();
+ final SparseFileContent b = script.getB();
+ final PrettyFormatter fmtA = PrettyFormatter.newFormatter(formatLanguage);
+ final PrettyFormatter fmtB = PrettyFormatter.newFormatter(formatLanguage);
+ final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
+ final SafeHtmlBuilder nc = new SafeHtmlBuilder();
+
+ fmtB.setShowWhiteSpaceErrors(true);
+ appendHeader(nc);
+ lines.add(null);
+
+ int lastB = 0;
+ final boolean ignoreWS = script.isIgnoreWhitespace();
+ for (final EditList.Hunk hunk : script.getHunks()) {
+ if (!hunk.isStartOfFile()) {
+ appendSkipLine(nc, hunk.getCurB() - lastB);
+ lines.add(null);
+ }
+
+ while (hunk.next()) {
+ if (hunk.isContextLine()) {
+ openLine(nc);
+ final SafeHtml ctx = fmtA.format(a.get(hunk.getCurA()));
+ appendLineText(nc, hunk.getCurA(), CONTEXT, ctx);
+ if (ignoreWS && b.contains(hunk.getCurB())) {
+ appendLineText(nc, hunk.getCurB(), CONTEXT, b, hunk.getCurB(), fmtB);
+ } else {
+ appendLineText(nc, hunk.getCurB(), CONTEXT, ctx);
+ }
+ closeLine(nc);
+ hunk.incBoth();
+ lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
+
+ } else if (hunk.isModifiedLine()) {
+ final boolean del = hunk.isDeletedA();
+ final boolean ins = hunk.isInsertedB();
+ openLine(nc);
+
+ if (del) {
+ appendLineText(nc, hunk.getCurA(), DELETE, a, hunk.getCurA(), fmtA);
+ hunk.incA();
+ } else {
+ appendLineNone(nc);
+ }
+
+ if (ins) {
+ appendLineText(nc, hunk.getCurB(), INSERT, b, hunk.getCurB(), fmtB);
+ hunk.incB();
+ } else {
+ appendLineNone(nc);
+ }
+
+ closeLine(nc);
+
+ if (del && ins) {
+ lines.add(new PatchLine(REPLACE, hunk.getCurA(), hunk.getCurB()));
+ } else if (del) {
+ lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
+ } else if (ins) {
+ lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
+ }
+ }
+ }
+ lastB = hunk.getCurB();
+ }
+ if (lastB != b.size()) {
+ appendSkipLine(nc, b.size() - lastB);
+ }
+ resetHtml(nc);
+ initScript(script);
+
+ for (int row = 0; row < lines.size(); row++) {
+ setRowItem(row, lines.get(row));
+ }
+ }
+
+ @Override
+ public void display(final CommentDetail cd) {
+ if (cd.isEmpty()) {
+ return;
+ }
+ setAccountInfoCache(cd.getAccounts());
+
+ for (int row = 0; row < table.getRowCount();) {
+ if (getRowItem(row) instanceof PatchLine) {
+ final PatchLine pLine = (PatchLine) getRowItem(row);
+ final List<PatchLineComment> fora = cd.getForA(pLine.getLineA());
+ final List<PatchLineComment> forb = cd.getForB(pLine.getLineB());
+ row++;
+
+ final Iterator<PatchLineComment> ai = fora.iterator();
+ final Iterator<PatchLineComment> bi = forb.iterator();
+ while (ai.hasNext() && bi.hasNext()) {
+ final PatchLineComment ac = ai.next();
+ final PatchLineComment bc = bi.next();
+ insertRow(row);
+ bindComment(row, COL_A, ac, !ai.hasNext());
+ bindComment(row, COL_B, bc, !bi.hasNext());
+ row++;
+ }
+
+ row = finish(ai, row, COL_A);
+ row = finish(bi, row, COL_B);
+ } else {
+ row++;
+ }
+ }
+ }
+
+ @Override
+ protected void insertRow(final int row) {
+ super.insertRow(row);
+ final CellFormatter fmt = table.getCellFormatter();
+ fmt.addStyleName(row, COL_A - 1, "LineNumber");
+ fmt.addStyleName(row, COL_A, "DiffText");
+ fmt.addStyleName(row, COL_B - 1, "LineNumber");
+ fmt.addStyleName(row, COL_B, "DiffText");
+ }
+
+ private int finish(final Iterator<PatchLineComment> i, int row, final int col) {
+ while (i.hasNext()) {
+ final PatchLineComment c = i.next();
+ insertRow(row);
+ bindComment(row, col, c, !i.hasNext());
+ row++;
+ }
+ return row;
+ }
+
+ private void appendHeader(final SafeHtmlBuilder m) {
+ m.openTr();
+
+ m.openTd();
+ m.addStyleName(S_ICON_CELL);
+ m.addStyleName("FileColumnHeader");
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName("FileColumnHeader");
+ m.addStyleName("LineNumber");
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName("FileColumnHeader");
+ m.setAttribute("width", "50%");
+ m.append(PatchUtil.C.patchHeaderOld());
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName("FileColumnHeader");
+ m.addStyleName("LineNumber");
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName("FileColumnHeader");
+ m.setAttribute("width", "50%");
+ m.append(PatchUtil.C.patchHeaderNew());
+ m.closeTd();
+
+ m.closeTr();
+ }
+
+ private void appendSkipLine(final SafeHtmlBuilder m, final int skipCnt) {
+ m.openTr();
+
+ m.openTd();
+ m.setStyleName(S_ICON_CELL);
+ m.closeTd();
+
+ m.openTd();
+ m.setStyleName("SkipLine");
+ m.setAttribute("colspan", 4);
+ m.append(PatchUtil.M.patchSkipRegion(skipCnt));
+ m.closeTd();
+ m.closeTr();
+ }
+
+ private void openLine(final SafeHtmlBuilder m) {
+ m.openTr();
+ m.setAttribute("valign", "top");
+
+ m.openTd();
+ m.setStyleName(S_ICON_CELL);
+ m.closeTd();
+ }
+
+ private SafeHtml appendLineText(final SafeHtmlBuilder m,
+ final int lineNumberMinusOne, final PatchLine.Type type,
+ final SparseFileContent src, final int i, final PrettyFormatter dst) {
+ final SafeHtml lineHtml = dst.format(src.get(i));
+ appendLineText(m, lineNumberMinusOne, type, lineHtml);
+ return lineHtml;
+ }
+
+ private void appendLineText(final SafeHtmlBuilder m,
+ final int lineNumberMinusOne, final PatchLine.Type type,
+ final SafeHtml lineHtml) {
+ m.openTd();
+ m.setStyleName("LineNumber");
+ m.append(lineNumberMinusOne + 1);
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName("FileLine");
+ m.addStyleName("FileLine-" + type.name());
+ m.append(lineHtml);
+ m.closeTd();
+ }
+
+ private void appendLineNone(final SafeHtmlBuilder m) {
+ m.openTd();
+ m.setStyleName("LineNumber");
+ m.closeTd();
+
+ m.openTd();
+ m.addStyleName("FileLine");
+ m.addStyleName("FileLineNone");
+ m.closeTd();
+ }
+
+ private void closeLine(final SafeHtmlBuilder m) {
+ m.closeTr();
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
new file mode 100644
index 0000000000..7e6805233b
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
@@ -0,0 +1,357 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.patches;
+
+import static com.google.gerrit.client.patches.PatchLine.Type.CONTEXT;
+import static com.google.gerrit.client.patches.PatchLine.Type.DELETE;
+import static com.google.gerrit.client.patches.PatchLine.Type.INSERT;
+
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.EditList;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.SparseFileContent;
+import com.google.gerrit.common.data.EditList.Hunk;
+import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.safehtml.client.PrettyFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+import com.google.gwtorm.client.KeyUtil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+public class UnifiedDiffTable extends AbstractPatchContentTable {
+ private static final int PC = 3;
+ private static final Comparator<PatchLineComment> BY_DATE =
+ new Comparator<PatchLineComment>() {
+ public int compare(final PatchLineComment o1, final PatchLineComment o2) {
+ return o1.getWrittenOn().compareTo(o2.getWrittenOn());
+ }
+ };
+
+ @Override
+ protected void onCellDoubleClick(final int row, final int column) {
+ if (getRowItem(row) instanceof PatchLine) {
+ final PatchLine pl = (PatchLine) getRowItem(row);
+ switch (pl.getType()) {
+ case DELETE:
+ case CONTEXT:
+ createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
+ break;
+ case INSERT:
+ createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onInsertComment(final PatchLine pl) {
+ final int row = getCurrentRow();
+ switch (pl.getType()) {
+ case DELETE:
+ case CONTEXT:
+ createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
+ break;
+ case INSERT:
+ createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
+ break;
+ }
+ }
+
+ private void appendImgTag(SafeHtmlBuilder nc, String url) {
+ nc.openElement("img");
+ nc.setAttribute("src", url);
+ nc.closeElement("img");
+ }
+
+ @Override
+ protected void render(final PatchScript script) {
+ final SparseFileContent a = script.getA();
+ final SparseFileContent b = script.getB();
+ final SafeHtmlBuilder nc = new SafeHtmlBuilder();
+ final PrettyFormatter fmtA = PrettyFormatter.newFormatter(formatLanguage);
+ final PrettyFormatter fmtB = PrettyFormatter.newFormatter(formatLanguage);
+
+ fmtB.setShowWhiteSpaceErrors(true);
+
+ // Display the patch header
+ for (final String line : script.getPatchHeader()) {
+ appendFileHeader(nc, line);
+ }
+
+ if (script.getDisplayMethodA() == DisplayMethod.IMG
+ || script.getDisplayMethodB() == DisplayMethod.IMG) {
+ final String rawBase = GWT.getHostPageBaseURL() + "cat/";
+
+ nc.openTr();
+ nc.setAttribute("valign", "center");
+ nc.setAttribute("align", "center");
+
+ nc.openTd();
+ nc.nbsp();
+ nc.closeTd();
+
+ nc.openTd();
+ nc.nbsp();
+ nc.closeTd();
+
+ nc.openTd();
+ nc.nbsp();
+ nc.closeTd();
+
+ nc.openTd();
+ if (script.getDisplayMethodA() == DisplayMethod.IMG) {
+ if (idSideA == null) {
+ appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^1");
+ } else {
+ Patch.Key k = new Patch.Key(idSideA, patchKey.get());
+ appendImgTag(nc, rawBase + KeyUtil.encode(k.toString()) + "^0");
+ }
+ }
+ if (script.getDisplayMethodB() == DisplayMethod.IMG) {
+ appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^0");
+ }
+ nc.closeTd();
+
+ nc.closeTr();
+ }
+
+ final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
+ for (final EditList.Hunk hunk : script.getHunks()) {
+ appendHunkHeader(nc, hunk);
+ while (hunk.next()) {
+ if (hunk.isContextLine()) {
+ openLine(nc);
+ appendLineNumber(nc, hunk.getCurA());
+ appendLineNumber(nc, hunk.getCurB());
+ appendLineText(nc, CONTEXT, a, hunk.getCurA(), fmtA, fmtB);
+ closeLine(nc);
+ hunk.incBoth();
+ lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
+
+ } else if (hunk.isDeletedA()) {
+ openLine(nc);
+ appendLineNumber(nc, hunk.getCurA());
+ padLineNumber(nc);
+ appendLineText(nc, DELETE, a, hunk.getCurA(), fmtA, fmtB);
+ closeLine(nc);
+ hunk.incA();
+ lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
+ if (a.size() == hunk.getCurA() && a.isMissingNewlineAtEnd())
+ appendNoLF(nc);
+
+ } else if (hunk.isInsertedB()) {
+ openLine(nc);
+ padLineNumber(nc);
+ appendLineNumber(nc, hunk.getCurB());
+ appendLineText(nc, INSERT, b, hunk.getCurB(), fmtA, fmtB);
+ closeLine(nc);
+ hunk.incB();
+ lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
+ if (b.size() == hunk.getCurB() && b.isMissingNewlineAtEnd())
+ appendNoLF(nc);
+ }
+ }
+ }
+ resetHtml(nc);
+ initScript(script);
+
+ int row = script.getPatchHeader().size();
+ final CellFormatter fmt = table.getCellFormatter();
+ final Iterator<PatchLine> iLine = lines.iterator();
+ while (iLine.hasNext()) {
+ final PatchLine l = iLine.next();
+ final String n = "DiffText-" + l.getType().name();
+ while (!fmt.getStyleName(row, PC).contains(n)) {
+ row++;
+ }
+ setRowItem(row++, l);
+ }
+ }
+
+ @Override
+ public void display(final CommentDetail cd) {
+ if (cd.isEmpty()) {
+ return;
+ }
+ setAccountInfoCache(cd.getAccounts());
+
+ final ArrayList<PatchLineComment> all = new ArrayList<PatchLineComment>();
+ for (int row = 0; row < table.getRowCount();) {
+ if (getRowItem(row) instanceof PatchLine) {
+ final PatchLine pLine = (PatchLine) getRowItem(row);
+ final List<PatchLineComment> fora = cd.getForA(pLine.getLineA());
+ final List<PatchLineComment> forb = cd.getForB(pLine.getLineB());
+ row++;
+
+ if (!fora.isEmpty() && !forb.isEmpty()) {
+ all.clear();
+ all.addAll(fora);
+ all.addAll(forb);
+ Collections.sort(all, BY_DATE);
+ row = insert(all, row);
+
+ } else if (!fora.isEmpty()) {
+ row = insert(fora, row);
+
+ } else if (!forb.isEmpty()) {
+ row = insert(forb, row);
+ }
+ } else {
+ row++;
+ }
+ }
+ }
+
+
+ @Override
+ protected void insertRow(final int row) {
+ super.insertRow(row);
+ final CellFormatter fmt = table.getCellFormatter();
+ fmt.addStyleName(row, PC - 2, "LineNumber");
+ fmt.addStyleName(row, PC - 1, "LineNumber");
+ fmt.addStyleName(row, PC, "DiffText");
+ }
+
+ private int insert(final List<PatchLineComment> in, int row) {
+ for (Iterator<PatchLineComment> ci = in.iterator(); ci.hasNext();) {
+ final PatchLineComment c = ci.next();
+ insertRow(row);
+ bindComment(row, PC, c, !ci.hasNext());
+ row++;
+ }
+ return row;
+ }
+
+ private void appendFileHeader(final SafeHtmlBuilder m, final String line) {
+ openLine(m);
+ padLineNumber(m);
+ padLineNumber(m);
+
+ m.openTd();
+ m.addStyleName("DiffText");
+ m.addStyleName("DiffText-FILE_HEADER");
+ m.append(line);
+ m.closeTd();
+ closeLine(m);
+ }
+
+ private void appendHunkHeader(final SafeHtmlBuilder m, final Hunk hunk) {
+ openLine(m);
+ padLineNumber(m);
+ padLineNumber(m);
+
+ m.openTd();
+ m.addStyleName("DiffText");
+ m.addStyleName("DiffText-HUNK_HEADER");
+ m.append("@@ -");
+ appendRange(m, hunk.getCurA() + 1, hunk.getEndA() - hunk.getCurA());
+ m.append(" +");
+ appendRange(m, hunk.getCurB() + 1, hunk.getEndB() - hunk.getCurB());
+ m.append(" @@");
+ m.closeTd();
+
+ closeLine(m);
+ }
+
+ private void appendRange(final SafeHtmlBuilder m, final int begin,
+ final int cnt) {
+ switch (cnt) {
+ case 0:
+ m.append(begin - 1);
+ m.append(",0");
+ break;
+
+ case 1:
+ m.append(begin);
+ break;
+
+ default:
+ m.append(begin);
+ m.append(',');
+ m.append(cnt);
+ break;
+ }
+ }
+
+ private void appendLineText(final SafeHtmlBuilder m,
+ final PatchLine.Type type, final SparseFileContent src, final int i,
+ final PrettyFormatter fmtA, final PrettyFormatter fmtB) {
+ final String text = src.get(i);
+ m.openTd();
+ m.addStyleName("DiffText");
+ m.addStyleName("DiffText-" + type.name());
+ switch (type) {
+ case CONTEXT:
+ m.nbsp();
+ m.append(fmtA.format(text));
+ fmtB.update(text);
+ break;
+ case DELETE:
+ m.append("-");
+ m.append(fmtA.format(text));
+ break;
+ case INSERT:
+ m.append("+");
+ m.append(fmtB.format(text));
+ break;
+ }
+ m.closeTd();
+ }
+
+ private void appendNoLF(final SafeHtmlBuilder m) {
+ openLine(m);
+ padLineNumber(m);
+ padLineNumber(m);
+ m.openTd();
+ m.addStyleName("DiffText");
+ m.addStyleName("DiffText-NO_LF");
+ m.append("\\ No newline at end of file");
+ m.closeTd();
+ closeLine(m);
+ }
+
+ private void openLine(final SafeHtmlBuilder m) {
+ m.openTr();
+ m.setAttribute("valign", "top");
+ m.openTd();
+ m.setStyleName(S_ICON_CELL);
+ m.closeTd();
+ }
+
+ private void closeLine(final SafeHtmlBuilder m) {
+ m.closeTr();
+ }
+
+ private void padLineNumber(final SafeHtmlBuilder m) {
+ m.openTd();
+ m.setStyleName("LineNumber");
+ m.closeTd();
+ }
+
+ private void appendLineNumber(final SafeHtmlBuilder m, final int idx) {
+ m.openTd();
+ m.setStyleName("LineNumber");
+ m.append(idx + 1);
+ m.closeTd();
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/redNot.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png
index 4e83a8fdb5..4e83a8fdb5 100644
--- a/src/main/java/com/google/gerrit/client/redNot.png
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/redNot.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
new file mode 100644
index 0000000000..fa028d75ba
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
@@ -0,0 +1,83 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.rpc;
+
+import com.google.gerrit.client.ErrorDialog;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.NotSignedInDialog;
+import com.google.gerrit.common.errors.NameAlreadyUsedException;
+import com.google.gerrit.common.errors.NoSuchAccountException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.rpc.InvocationException;
+import com.google.gwtjsonrpc.client.JsonUtil;
+import com.google.gwtjsonrpc.client.RemoteJsonException;
+import com.google.gwtjsonrpc.client.ServerUnavailableException;
+
+/** Abstract callback handling generic error conditions automatically */
+public abstract class GerritCallback<T> implements AsyncCallback<T> {
+ public void onFailure(final Throwable caught) {
+ if (isNotSignedIn(caught) || isInvalidXSRF(caught)) {
+ new NotSignedInDialog().center();
+
+ } else if (isNoSuchEntity(caught)) {
+ new ErrorDialog(Gerrit.C.notFoundBody()).center();
+
+ } else if (isNoSuchAccount(caught)) {
+ final String msg = caught.getMessage();
+ final String who = msg.substring(NoSuchAccountException.MESSAGE.length());
+ final ErrorDialog d = new ErrorDialog(Gerrit.M.noSuchAccountMessage(who));
+ d.setText(Gerrit.C.noSuchAccountTitle());
+ d.center();
+
+ } else if (isNameAlreadyUsed(caught)) {
+ new ErrorDialog(Gerrit.C.nameAlreadyUsedBody()).center();
+
+ } else if (caught instanceof ServerUnavailableException) {
+ new ErrorDialog(RpcConstants.C.errorServerUnavailable()).center();
+
+ } else {
+ GWT.log(getClass().getName() + " caught " + caught, caught);
+ new ErrorDialog(caught).center();
+ }
+ }
+
+ private static boolean isInvalidXSRF(final Throwable caught) {
+ return caught instanceof InvocationException
+ && caught.getMessage().equals(JsonUtil.ERROR_INVALID_XSRF);
+ }
+
+ private static boolean isNotSignedIn(final Throwable caught) {
+ return caught instanceof RemoteJsonException
+ && caught.getMessage().equals(NotSignedInException.MESSAGE);
+ }
+
+ protected static boolean isNoSuchEntity(final Throwable caught) {
+ return caught instanceof RemoteJsonException
+ && caught.getMessage().equals(NoSuchEntityException.MESSAGE);
+ }
+
+ private static boolean isNoSuchAccount(final Throwable caught) {
+ return caught instanceof RemoteJsonException
+ && caught.getMessage().startsWith(NoSuchAccountException.MESSAGE);
+ }
+
+ private static boolean isNameAlreadyUsed(final Throwable caught) {
+ return caught instanceof RemoteJsonException
+ && caught.getMessage().equals(NameAlreadyUsedException.MESSAGE);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java
index 89e9367674..89e9367674 100644
--- a/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.java
diff --git a/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties
index e8695b1846..e8695b1846 100644
--- a/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/RpcConstants.properties
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
new file mode 100644
index 0000000000..bfd30f71b1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.rpc;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.NotFoundScreen;
+import com.google.gerrit.client.ui.Screen;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+
+/** Callback switching {@link NoSuchEntityException} to {@link NotFoundScreen} */
+public abstract class ScreenLoadCallback<T> extends GerritCallback<T> {
+ private final Screen screen;
+
+ public ScreenLoadCallback(final Screen s) {
+ screen = s;
+ }
+
+ public final void onSuccess(final T result) {
+ if (screen.isAttached()) {
+ preDisplay(result);
+ screen.display();
+ postDisplay();
+ }
+ }
+
+ protected abstract void preDisplay(T result);
+
+ protected void postDisplay() {
+ }
+
+ @Override
+ public void onFailure(final Throwable caught) {
+ if (isNoSuchEntity(caught)) {
+ Gerrit.display(new NotFoundScreen());
+ } else {
+ super.onFailure(caught);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/starFilled.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/client/starFilled.gif
index 77619f0dc3..77619f0dc3 100644
--- a/src/main/java/com/google/gerrit/client/starFilled.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/starFilled.gif
Binary files differ
diff --git a/src/main/java/com/google/gerrit/client/starOpen.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/client/starOpen.gif
index e8dc0a3aa2..e8dc0a3aa2 100644
--- a/src/main/java/com/google/gerrit/client/starOpen.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/starOpen.gif
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java
new file mode 100644
index 0000000000..a9ddfbc85b
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.changes.AccountDashboardScreen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.reviewdb.Account;
+
+/** Link to any user's account dashboard. */
+public class AccountDashboardLink extends DirectScreenLink {
+ /** Create a link after locating account details from an active cache. */
+ public static AccountDashboardLink link(final AccountInfoCache cache,
+ final Account.Id id) {
+ final AccountInfo ai = cache.get(id);
+ return ai != null ? new AccountDashboardLink(ai) : null;
+ }
+
+ private Account.Id accountId;
+
+ public AccountDashboardLink(final AccountInfo ai) {
+ this(FormatUtil.name(ai), ai);
+ }
+
+ public AccountDashboardLink(final String text, final AccountInfo ai) {
+ this(text, ai.getId());
+ setTitle(FormatUtil.nameEmail(ai));
+ }
+
+ public AccountDashboardLink(final String text, final Account.Id ai) {
+ super(text, PageLinks.toAccountDashboard(ai));
+ addStyleName("gerrit-AccountName");
+ accountId = ai;
+ }
+
+ @Override
+ protected Screen createScreen() {
+ return new AccountDashboardScreen(accountId);
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
new file mode 100644
index 0000000000..41da6c3969
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.RpcStatus;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gwt.user.client.ui.SuggestOracle;
+import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Suggestion Oracle for AccountGroup entities. */
+public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
+ @Override
+ public void onRequestSuggestions(final Request req, final Callback callback) {
+ RpcStatus.hide(new Runnable() {
+ public void run() {
+ SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
+ new GerritCallback<List<AccountGroup>>() {
+ public void onSuccess(final List<AccountGroup> result) {
+ final ArrayList<AccountGroupSuggestion> r =
+ new ArrayList<AccountGroupSuggestion>(result.size());
+ for (final AccountGroup p : result) {
+ r.add(new AccountGroupSuggestion(p));
+ }
+ callback.onSuggestionsReady(req, new Response(r));
+ }
+ });
+ }
+ });
+ }
+
+ private static class AccountGroupSuggestion implements
+ SuggestOracle.Suggestion {
+ private final AccountGroup info;
+
+ AccountGroupSuggestion(final AccountGroup k) {
+ info = k;
+ }
+
+ public String getDisplayString() {
+ return info.getName();
+ }
+
+ public String getReplacementString() {
+ return info.getName();
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/AccountScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java
index 59ff9fe3cb..59ff9fe3cb 100644
--- a/src/main/java/com/google/gerrit/client/ui/AccountScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountScreen.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
new file mode 100644
index 0000000000..441878f41a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.RpcStatus;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gwt.user.client.ui.SuggestOracle;
+import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Suggestion Oracle for Account entities. */
+public class AccountSuggestOracle extends HighlightSuggestOracle {
+ @Override
+ public void onRequestSuggestions(final Request req, final Callback callback) {
+ RpcStatus.hide(new Runnable() {
+ public void run() {
+ SuggestUtil.SVC.suggestAccount(req.getQuery(), req.getLimit(),
+ new GerritCallback<List<AccountInfo>>() {
+ public void onSuccess(final List<AccountInfo> result) {
+ final ArrayList<AccountSuggestion> r =
+ new ArrayList<AccountSuggestion>(result.size());
+ for (final AccountInfo p : result) {
+ r.add(new AccountSuggestion(p));
+ }
+ callback.onSuggestionsReady(req, new Response(r));
+ }
+ });
+ }
+ });
+ }
+
+ private static class AccountSuggestion implements SuggestOracle.Suggestion {
+ private final AccountInfo info;
+
+ AccountSuggestion(final AccountInfo k) {
+ info = k;
+ }
+
+ public String getDisplayString() {
+ return FormatUtil.nameEmail(info);
+ }
+
+ public String getReplacementString() {
+ return FormatUtil.nameEmail(info);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java
index 858ae36f00..858ae36f00 100644
--- a/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AddMemberBox.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
new file mode 100644
index 0000000000..1e8efe1cce
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.changes.ChangeScreen;
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+
+public class ChangeLink extends DirectScreenLink {
+ public static String permalink(final Change.Id c) {
+ return GWT.getHostPageBaseURL() + c.get();
+ }
+
+ protected Change.Id id;
+ private ChangeInfo info;
+
+ public ChangeLink(final String text, final Change.Id c) {
+ super(text, PageLinks.toChange(c));
+ DOM.setElementProperty(getElement(), "href", permalink(c));
+ id = c;
+ }
+
+ public ChangeLink(final String text, final ChangeInfo c) {
+ this(text, c.getId());
+ info = c;
+ }
+
+ @Override
+ protected Screen createScreen() {
+ return info != null ? new ChangeScreen(info) : new ChangeScreen(id);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
index a12625f265..a12625f265 100644
--- a/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommandMenuItem.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
new file mode 100644
index 0000000000..eec83768e1
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
@@ -0,0 +1,193 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.FormatUtil;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.DoubleClickEvent;
+import com.google.gwt.event.dom.client.DoubleClickHandler;
+import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HasHorizontalAlignment;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+import com.google.gwtexpui.safehtml.client.SafeHtml;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
+
+import java.util.Date;
+
+public class CommentPanel extends Composite implements HasDoubleClickHandlers {
+ private static final int SUMMARY_LENGTH = 75;
+ private final FlexTable header;
+ private final InlineLabel messageSummary;
+ private final FlowPanel content;
+ private final DoubleClickHTML messageText;
+ private FlowPanel buttons;
+ private boolean recent;
+
+ public CommentPanel(final AccountInfo author, final Date when, String message) {
+ this();
+
+ setMessageText(message);
+ setAuthorNameText(FormatUtil.name(author));
+ setDateText(FormatUtil.shortFormat(when));
+
+ final CellFormatter fmt = header.getCellFormatter();
+ fmt.getElement(0, 0).setTitle(FormatUtil.nameEmail(author));
+ fmt.getElement(0, 2).setTitle(FormatUtil.mediumFormat(when));
+ }
+
+ protected CommentPanel() {
+ final FlowPanel body = new FlowPanel();
+ initWidget(body);
+ setStyleName("gerrit-CommentPanel");
+
+ messageSummary = new InlineLabel();
+ messageSummary.setStyleName("gerrit-CommentPanel-Summary");
+
+ header = new FlexTable();
+ header.setStyleName("gerrit-CommentPanel-Header");
+ header.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ setOpen(!isOpen());
+ }
+ });
+ header.setText(0, 0, "");
+ header.setWidget(0, 1, messageSummary);
+ header.setText(0, 2, "");
+ final CellFormatter fmt = header.getCellFormatter();
+ fmt.setStyleName(0, 0, "gerrit-CommentPanel-AuthorCell");
+ fmt.setStyleName(0, 1, "gerrit-CommentPanel-SummaryCell");
+ fmt.setStyleName(0, 2, "gerrit-CommentPanel-DateCell");
+ fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
+ body.add(header);
+
+ content = new FlowPanel();
+ content.setStyleName("gerrit-CommentPanel-Content");
+ content.setVisible(false);
+ body.add(content);
+
+ messageText = new DoubleClickHTML();
+ messageText.setStyleName("gerrit-CommentPanel-Message");
+ content.add(messageText);
+ }
+
+ @Override
+ public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
+ return messageText.addDoubleClickHandler(handler);
+ }
+
+ protected void setMessageText(String message) {
+ if (message == null) {
+ message = "";
+ } else {
+ message = message.trim();
+ }
+
+ messageSummary.setText(summarize(message));
+ SafeHtml.set(messageText, new SafeHtmlBuilder().append(message).wikify()
+ .replaceAll(Gerrit.getConfig().getCommentLinks()));
+ }
+
+ public void setAuthorNameText(final String nameText) {
+ header.setText(0, 0, nameText);
+ }
+
+ protected void setDateText(final String dateText) {
+ header.setText(0, 2, dateText);
+ }
+
+ protected void setMessageTextVisible(final boolean show) {
+ messageText.setVisible(show);
+ }
+
+ protected void addContent(final Widget w) {
+ if (buttons != null) {
+ content.insert(w, content.getWidgetIndex(buttons));
+ } else {
+ content.add(w);
+ }
+ }
+
+ protected Panel getButtonPanel() {
+ if (buttons == null) {
+ buttons = new FlowPanel();
+ buttons.setStyleName("gerrit-CommentPanel-Buttons");
+ content.add(buttons);
+ }
+ return buttons;
+ }
+
+ private static String summarize(final String message) {
+ if (message.length() < SUMMARY_LENGTH) {
+ return message;
+ }
+
+ int p = 0;
+ final StringBuilder r = new StringBuilder();
+ while (r.length() < SUMMARY_LENGTH) {
+ final int e = message.indexOf(' ', p);
+ if (e < 0) {
+ break;
+ }
+
+ final String word = message.substring(p, e).trim();
+ if (SUMMARY_LENGTH <= r.length() + word.length() + 1) {
+ break;
+ }
+ if (r.length() > 0) {
+ r.append(' ');
+ }
+ r.append(word);
+ p = e + 1;
+ }
+ r.append(" \u2026");
+ return r.toString();
+ }
+
+ public boolean isOpen() {
+ return content.isVisible();
+ }
+
+ public void setOpen(final boolean open) {
+ messageSummary.setVisible(!open);
+ content.setVisible(open);
+ }
+
+ public boolean isRecent() {
+ return recent;
+ }
+
+ public void setRecent(final boolean r) {
+ recent = r;
+ }
+
+ private static class DoubleClickHTML extends HTML implements
+ HasDoubleClickHandlers {
+ public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
+ return addDomHandler(handler, DoubleClickEvent.getType());
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java
index ff4eee0710..ff4eee0710 100644
--- a/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ComplexDisclosurePanel.java
diff --git a/src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java
index 4fc4456925..4fc4456925 100644
--- a/src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/DirectScreenLink.java
diff --git a/src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java
index 43721d168d..43721d168d 100644
--- a/src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ExpandAllCommand.java
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
index 5281f32f6d..5281f32f6d 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTable.java
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
index c72969e5b6..c72969e5b6 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImpl.java
diff --git a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
index 17e8ddd723..17e8ddd723 100644
--- a/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/FancyFlexTableImplIE6.java
diff --git a/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
index 0fef53b040..0fef53b040 100644
--- a/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuBar.java
diff --git a/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
index 90cbbc08b6..90cbbc08b6 100644
--- a/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/LinkMenuItem.java
diff --git a/src/main/java/com/google/gerrit/client/ui/NavigationTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java
index 7e799b48e8..7e799b48e8 100644
--- a/src/main/java/com/google/gerrit/client/ui/NavigationTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NavigationTable.java
diff --git a/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java
index 44207621b0..44207621b0 100644
--- a/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/NeedsSignInKeyCommand.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PatchLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PatchLink.java
new file mode 100644
index 0000000000..baa1d1121a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/PatchLink.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.changes.PatchTable;
+import com.google.gerrit.client.patches.PatchScreen;
+import com.google.gerrit.reviewdb.Patch;
+
+public abstract class PatchLink extends DirectScreenLink {
+ protected Patch.Key patchKey;
+ protected int patchIndex;
+ protected PatchTable parentPatchTable;
+
+ /**
+ * @param text The text of this link
+ * @param patchKey The key for this patch
+ * @param patchIndex The index of the current patch in the patch set
+ * @param historyToken The history token
+ * @param parentPatchTable The table used to display this link
+ */
+ public PatchLink(final String text, final Patch.Key patchKey, final int patchIndex,
+ final String historyToken, PatchTable parentPatchTable) {
+ super(text, historyToken);
+ this.patchKey = patchKey;
+ this.patchIndex = patchIndex;
+ this.parentPatchTable = parentPatchTable;
+ }
+
+ public static class SideBySide extends PatchLink {
+ public SideBySide(final String text, final Patch.Key patchKey, final int patchIndex,
+ PatchTable parentPatchTable) {
+ super(text, patchKey, patchIndex, HistoryHandler.toPatchSideBySide(patchKey), parentPatchTable);
+ }
+
+ @Override
+ protected Screen createScreen() {
+ return new PatchScreen.SideBySide(patchKey, patchIndex, parentPatchTable);
+ }
+ }
+
+ public static class Unified extends PatchLink {
+ public Unified(final String text, final Patch.Key patchKey, final int patchIndex,
+ PatchTable parentPatchTable) {
+ super(text, patchKey, patchIndex, HistoryHandler.toPatchUnified(patchKey), parentPatchTable);
+ }
+
+ @Override
+ protected Screen createScreen() {
+ return new PatchScreen.Unified(patchKey, patchIndex, parentPatchTable);
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
new file mode 100644
index 0000000000..c5b37ae741
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.HistoryHandler;
+import com.google.gerrit.client.changes.ByProjectAbandonedChangesScreen;
+import com.google.gerrit.client.changes.ByProjectMergedChangesScreen;
+import com.google.gerrit.client.changes.ByProjectOpenChangesScreen;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.Change.Status;
+
+/** Link to the open changes of a project. */
+public class ProjectLink extends DirectScreenLink {
+ private Project.NameKey project;
+ private Status status;
+
+ public ProjectLink(final Project.NameKey proj, Change.Status stat) {
+ this(proj.get(), proj, stat);
+ }
+
+ public ProjectLink(final String text, final Project.NameKey proj,
+ Change.Status stat) {
+ super(text, HistoryHandler.toProject(proj, stat));
+ status = stat;
+ project = proj;
+ }
+
+ @Override
+ protected Screen createScreen() {
+ switch (status) {
+ case ABANDONED:
+ return new ByProjectAbandonedChangesScreen(project, "n,z");
+
+ case MERGED:
+ return new ByProjectMergedChangesScreen(project, "n,z");
+
+ case NEW:
+ case SUBMITTED:
+ default:
+ return new ByProjectOpenChangesScreen(project, "n,z");
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
new file mode 100644
index 0000000000..49fe165647
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.RpcStatus;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gwt.user.client.ui.SuggestOracle;
+import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Suggestion Oracle for Project.NameKey entities. */
+public class ProjectNameSuggestOracle extends HighlightSuggestOracle {
+ @Override
+ public void onRequestSuggestions(final Request req, final Callback callback) {
+ RpcStatus.hide(new Runnable() {
+ public void run() {
+ SuggestUtil.SVC.suggestProjectNameKey(req.getQuery(), req.getLimit(),
+ new GerritCallback<List<Project.NameKey>>() {
+ public void onSuccess(final List<Project.NameKey> result) {
+ final ArrayList<ProjectNameSuggestion> r =
+ new ArrayList<ProjectNameSuggestion>(result.size());
+ for (final Project.NameKey p : result) {
+ r.add(new ProjectNameSuggestion(p));
+ }
+ callback.onSuggestionsReady(req, new Response(r));
+ }
+ });
+ }
+ });
+ }
+
+ private static class ProjectNameSuggestion implements
+ SuggestOracle.Suggestion {
+ private final Project.NameKey key;
+
+ ProjectNameSuggestion(final Project.NameKey k) {
+ key = k;
+ }
+
+ public String getDisplayString() {
+ return key.get();
+ }
+
+ public String getReplacementString() {
+ return key.get();
+ }
+ }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
new file mode 100644
index 0000000000..2cffcc0b0f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Screen.java
@@ -0,0 +1,101 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.PageLinks;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.InlineLabel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.user.client.View;
+
+public abstract class Screen extends View {
+ private FlowPanel header;
+ private InlineLabel headerText;
+ private FlowPanel body;
+ private boolean requiresSignIn;
+ private String windowTitle;
+
+ protected Screen() {
+ initWidget(new FlowPanel());
+ setStyleName("gerrit-Screen");
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ if (header == null) {
+ onInitUI();
+ }
+ }
+
+ public void registerKeys() {
+ }
+
+ protected void onInitUI() {
+ final FlowPanel me = (FlowPanel) getWidget();
+ me.add(header = new FlowPanel());
+ me.add(body = new FlowPanel());
+
+ header.setStyleName("gerrit-ScreenHeader");
+ header.add(headerText = new InlineLabel());
+ }
+
+ protected void setWindowTitle(final String text) {
+ windowTitle = text;
+ Gerrit.setWindowTitle(this, text);
+ }
+
+ protected void setPageTitle(final String text) {
+ final String old = headerText.getText();
+ headerText.setText(text);
+ if (windowTitle == null || windowTitle == old) {
+ setWindowTitle(text);
+ }
+ }
+
+ protected void insertTitleWidget(final Widget w) {
+ header.insert(w, 0);
+ }
+
+ protected final void add(final Widget w) {
+ body.add(w);
+ }
+
+ /** Set whether or not {@link Gerrit#isSignedIn()} must be true. */
+ public final void setRequiresSignIn(final boolean b) {
+ requiresSignIn = b;
+ }
+
+ /** Does {@link Gerrit#isSignedIn()} have to be true to be on this screen? */
+ public final boolean isRequiresSignIn() {
+ return requiresSignIn;
+ }
+
+ /** Invoked if this screen is the current screen and the user signs out. */
+ public void onSignOut() {
+ if (isRequiresSignIn()) {
+ History.newItem(PageLinks.ALL_OPEN);
+ }
+ }
+
+ public void onShowView() {
+ if (windowTitle != null) {
+ Gerrit.setWindowTitle(this, windowTitle);
+ }
+ registerKeys();
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/SmallHeading.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java
index 153af6c0a1..153af6c0a1 100644
--- a/src/main/java/com/google/gerrit/client/ui/SmallHeading.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SmallHeading.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java
new file mode 100644
index 0000000000..6e81b71b0c
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.client.ui;
+
+import com.google.gerrit.common.data.SuggestService;
+import com.google.gwt.core.client.GWT;
+import com.google.gwtjsonrpc.client.JsonUtil;
+
+public class SuggestUtil {
+ public static final SuggestService SVC;
+
+ static {
+ SVC = GWT.create(SuggestService.class);
+ JsonUtil.bind(SVC, "rpc/SuggestService");
+ }
+
+ private SuggestUtil() {
+ }
+}
diff --git a/src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java
index 63270df8fa..63270df8fa 100644
--- a/src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/TextSaveButtonListener.java
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/common/Version.java b/gerrit-gwtui/src/main/java/com/google/gerrit/common/Version.java
new file mode 100644
index 0000000000..d831c7158e
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/common/Version.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common;
+
+import com.google.gwt.i18n.client.Constants;
+
+/**
+ * Automatically generated version code.
+ * <p>
+ * Do not translate this constants interface.
+ */
+public interface Version extends Constants {
+ String version();
+}
diff --git a/src/main/java/com/google/gerrit/public/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/public/gerrit.css
index ebda06baf5..ebda06baf5 100644
--- a/src/main/java/com/google/gerrit/public/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/public/gerrit.css
diff --git a/src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif b/gerrit-gwtui/src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif
index cde836c893..cde836c893 100644
--- a/src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/public/openidlogin_bg1.cache.gif
Binary files differ
diff --git a/gerrit-gwtui/src/main/webapp/WEB-INF/web.xml b/gerrit-gwtui/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..494f0c1623
--- /dev/null
+++ b/gerrit-gwtui/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app>
+</web-app>
diff --git a/gerrit-httpd/.gitignore b/gerrit-httpd/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-httpd/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-httpd/.settings/org.eclipse.core.resources.prefs b/gerrit-httpd/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-httpd/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-httpd/.settings/org.eclipse.core.runtime.prefs b/gerrit-httpd/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-httpd/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-httpd/.settings/org.eclipse.jdt.core.prefs b/gerrit-httpd/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-httpd/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-httpd/.settings/org.eclipse.jdt.ui.prefs b/gerrit-httpd/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-httpd/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-httpd/pom.xml b/gerrit-httpd/pom.xml
new file mode 100644
index 0000000000..acda03c7a5
--- /dev/null
+++ b/gerrit-httpd/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-httpd</artifactId>
+ <name>Gerrit Code Review - HTTPd</name>
+
+ <description>
+ Servlet context for components run inside of an HTTP environment.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-servlet</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>eu.medsea.mimeutil</groupId>
+ <artifactId>mime-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openid4java</groupId>
+ <artifactId>openid4java-consumer</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-server</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java
new file mode 100644
index 0000000000..3bcdcd25e8
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CookieBase64.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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.
+//
+// This code is based heavily on Robert Harder's <rob@iharder.net>
+// public domain Base64 class, version 2.1.
+//
+
+package com.google.gerrit.httpd;
+
+/** Base64 encoder which uses a language safe within HTTP cookies. */
+class CookieBase64 {
+ private static final char[] enc;
+
+ static {
+ enc = new char[64];
+ int o = 0;
+ o = fill(enc, o, 'a', 'z');
+ o = fill(enc, o, 'A', 'Z');
+ o = fill(enc, o, '0', '9');
+ enc[o++] = '-';
+ enc[o++] = '.';
+ }
+
+ private static int fill(final char[] out, int o, final char f, final int l) {
+ for (char c = f; c <= l; c++)
+ out[o++] = c;
+ return o;
+ }
+
+ static String encode(final byte[] in) {
+ final StringBuilder out = new StringBuilder(in.length * 4 / 3);
+ final int len2 = in.length - 2;
+ int d = 0;
+ for (; d < len2; d += 3) {
+ encode3to4(out, in, d, 3);
+ }
+ if (d < in.length) {
+ encode3to4(out, in, d, in.length - d);
+ }
+ return out.toString();
+ }
+
+ private static void encode3to4(final StringBuilder out, final byte[] in,
+ final int inOffset, final int numSigBytes) {
+ // 1 2 3
+ // 01234567890123456789012345678901 Bit position
+ // --------000000001111111122222222 Array position from threeBytes
+ // --------| || || || | Six bit groups to index ALPHABET
+ // >>18 >>12 >> 6 >> 0 Right shift necessary
+ // 0x3f 0x3f 0x3f Additional AND
+
+ // Create buffer with zero-padding if there are only one or two
+ // significant bytes passed in the array.
+ // We have to shift left 24 in order to flush out the 1's that appear
+ // when Java treats a value as negative that is cast from a byte to an int.
+ //
+ int inBuff = ( numSigBytes > 0 ? ((in[ inOffset ] << 24) >>> 8) : 0 )
+ | ( numSigBytes > 1 ? ((in[ inOffset + 1 ] << 24) >>> 16) : 0 )
+ | ( numSigBytes > 2 ? ((in[ inOffset + 2 ] << 24) >>> 24) : 0 );
+
+ switch (numSigBytes) {
+ case 3:
+ out.append(enc[(inBuff >>> 18)]);
+ out.append(enc[(inBuff >>> 12) & 0x3f]);
+ out.append(enc[(inBuff >>> 6) & 0x3f]);
+ out.append(enc[(inBuff) & 0x3f]);
+ break;
+
+ case 2:
+ out.append(enc[(inBuff >>> 18)]);
+ out.append(enc[(inBuff >>> 12) & 0x3f]);
+ out.append(enc[(inBuff >>> 6) & 0x3f]);
+ break;
+
+ case 1:
+ out.append(enc[(inBuff >>> 18)]);
+ out.append(enc[(inBuff >>> 12) & 0x3f]);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ private CookieBase64() {
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
new file mode 100644
index 0000000000..a56addd3eb
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -0,0 +1,126 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.common.data.GitwebLink;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.server.contact.ContactStore;
+import com.google.gerrit.server.mail.EmailSender;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.gwtexpui.safehtml.client.RegexFindReplace;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+class GerritConfigProvider implements Provider<GerritConfig> {
+ private final Realm realm;
+ private final Config cfg;
+ private final String canonicalWebUrl;
+ private final AuthConfig authConfig;
+ private final Project.NameKey wildProject;
+ private final SshInfo sshInfo;
+ private final ApprovalTypes approvalTypes;
+
+ private EmailSender emailSender;
+ private final ContactStore contactStore;
+
+ @Inject
+ GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc,
+ @CanonicalWebUrl @Nullable final String cwu, final AuthConfig ac,
+ @WildProjectName final Project.NameKey wp, final SshInfo si,
+ final ApprovalTypes at, final ContactStore cs) {
+ realm = r;
+ cfg = gsc;
+ canonicalWebUrl = cwu;
+ authConfig = ac;
+ sshInfo = si;
+ wildProject = wp;
+ approvalTypes = at;
+ contactStore = cs;
+ }
+
+ @Inject(optional = true)
+ void setEmailSender(final EmailSender d) {
+ emailSender = d;
+ }
+
+ private GerritConfig create() {
+ final GerritConfig config = new GerritConfig();
+ config.setCanonicalUrl(canonicalWebUrl);
+ config.setUseContributorAgreements(cfg.getBoolean("auth",
+ "contributoragreements", false));
+ config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
+ config.setUseRepoDownload(cfg.getBoolean("repo", null,
+ "showdownloadcommand", false));
+ config.setUseContactInfo(contactStore != null && contactStore.isEnabled());
+ config.setAuthType(authConfig.getAuthType());
+ config.setWildProject(wildProject);
+ config.setApprovalTypes(approvalTypes);
+
+ final Set<Account.FieldName> fields = new HashSet<Account.FieldName>();
+ for (final Account.FieldName n : Account.FieldName.values()) {
+ if (realm.allowsEdit(n)) {
+ fields.add(n);
+ }
+ }
+ if (emailSender != null && emailSender.isEnabled()) {
+ fields.add(Account.FieldName.REGISTER_NEW_EMAIL);
+ }
+ config.setEditableAccountFields(fields);
+
+ final String gitwebUrl = cfg.getString("gitweb", null, "url");
+ if (gitwebUrl != null) {
+ config.setGitwebLink(new GitwebLink(gitwebUrl));
+ }
+
+ if (sshInfo != null && !sshInfo.getHostKeys().isEmpty()) {
+ config.setSshdAddress(sshInfo.getHostKeys().get(0).getHost());
+ }
+
+ ArrayList<String> commentLinkNames =
+ new ArrayList<String>(cfg.getSubsections("CommentLink"));
+ ArrayList<RegexFindReplace> commentLinks =
+ new ArrayList<RegexFindReplace>(commentLinkNames.size());
+ for (String commentLinkName : commentLinkNames) {
+ String match = cfg.getString("commentlink", commentLinkName, "match");
+ String link =
+ "<a href=\"" + cfg.getString("commentlink", commentLinkName, "link")
+ + "\">$&</a>";
+ commentLinks.add(new RegexFindReplace(match, link));
+ }
+ config.setCommentLinks(commentLinks);
+
+ return config;
+ }
+
+ @Override
+ public GerritConfig get() {
+ return create();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
new file mode 100644
index 0000000000..a675e4f917
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
@@ -0,0 +1,245 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.zip.GZIPOutputStream;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+/** Utility functions to deal with HTML using W3C DOM operations. */
+public class HtmlDomUtil {
+ /** Standard character encoding we prefer (UTF-8). */
+ public static final String ENC = "UTF-8";
+
+ /** DOCTYPE for a standards mode HTML document. */
+ public static final String HTML_STRICT =
+ "-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd";
+
+ /** Convert a document to a UTF-8 byte sequence. */
+ public static byte[] toUTF8(final Document hostDoc) throws IOException {
+ return toString(hostDoc).getBytes(ENC);
+ }
+
+ /** Compress the document. */
+ public static byte[] compress(final byte[] raw) throws IOException {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final GZIPOutputStream gz = new GZIPOutputStream(out);
+ gz.write(raw);
+ gz.finish();
+ gz.flush();
+ return out.toByteArray();
+ }
+
+ /** Convert a document to a String, assuming later encoding to UTF-8. */
+ public static String toString(final Document hostDoc) throws IOException {
+ try {
+ final StringWriter out = new StringWriter();
+ final DOMSource domSource = new DOMSource(hostDoc);
+ final StreamResult streamResult = new StreamResult(out);
+ final TransformerFactory tf = TransformerFactory.newInstance();
+ final Transformer serializer = tf.newTransformer();
+ serializer.setOutputProperty(OutputKeys.ENCODING, ENC);
+ serializer.setOutputProperty(OutputKeys.METHOD, "html");
+ serializer.setOutputProperty(OutputKeys.INDENT, "no");
+ serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
+ HtmlDomUtil.HTML_STRICT);
+ serializer.transform(domSource, streamResult);
+ return out.toString();
+ } catch (TransformerConfigurationException e) {
+ final IOException r = new IOException("Error transforming page");
+ r.initCause(e);
+ throw r;
+ } catch (TransformerException e) {
+ final IOException r = new IOException("Error transforming page");
+ r.initCause(e);
+ throw r;
+ }
+ }
+
+ /** Find an element by its "id" attribute; null if no element is found. */
+ public static Element find(final Node parent, final String name) {
+ final NodeList list = parent.getChildNodes();
+ for (int i = 0; i < list.getLength(); i++) {
+ final Node n = list.item(i);
+ if (n instanceof Element) {
+ final Element e = (Element) n;
+ if (name.equals(e.getAttribute("id"))) {
+ return e;
+ }
+ }
+ final Element r = find(n, name);
+ if (r != null) {
+ return r;
+ }
+ }
+ return null;
+ }
+
+ /** Append an HTML &lt;input type="hidden"&gt; to the form. */
+ public static void addHidden(final Element form, final String name,
+ final String value) {
+ final Element in = form.getOwnerDocument().createElement("input");
+ in.setAttribute("type", "hidden");
+ in.setAttribute("name", name);
+ in.setAttribute("value", value);
+ form.appendChild(in);
+ }
+
+ /** Clone a document so it can be safely modified on a per-request basis. */
+ public static Document clone(final Document doc) throws IOException {
+ final Document d;
+ try {
+ d = newBuilder().newDocument();
+ } catch (ParserConfigurationException e) {
+ throw new IOException("Cannot clone document");
+ }
+ final Node n = d.importNode(doc.getDocumentElement(), true);
+ d.appendChild(n);
+ return d;
+ }
+
+ /** Parse an XHTML file from our CLASSPATH and return the instance. */
+ public static Document parseFile(final Class<?> context, final String name)
+ throws IOException {
+ final InputStream in;
+
+ in = context.getResourceAsStream(name);
+ if (in == null) {
+ return null;
+ }
+ try {
+ try {
+ try {
+ return newBuilder().parse(in);
+ } catch (SAXException e) {
+ throw new IOException("Error reading " + name, e);
+ } catch (ParserConfigurationException e) {
+ throw new IOException("Error reading " + name, e);
+ }
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new IOException("Error reading " + name, e);
+ }
+ }
+
+ /** Read a Read a UTF-8 text file from our CLASSPATH and return it. */
+ public static String readFile(final Class<?> context, final String name)
+ throws IOException {
+ final InputStream in = context.getResourceAsStream(name);
+ if (in == null) {
+ return null;
+ }
+ try {
+ return asString(in);
+ } catch (IOException e) {
+ throw new IOException("Error reading " + name, e);
+ }
+ }
+
+ /** Parse an XHTML file from the local drive and return the instance. */
+ public static Document parseFile(final File parentDir, final String name)
+ throws IOException {
+ if (parentDir == null) {
+ return null;
+ }
+ final File path = new File(parentDir, name);
+ try {
+ final InputStream in = new FileInputStream(path);
+ try {
+ try {
+ return newBuilder().parse(in);
+ } catch (SAXException e) {
+ throw new IOException("Error reading " + path, e);
+ } catch (ParserConfigurationException e) {
+ throw new IOException("Error reading " + path, e);
+ }
+ } finally {
+ in.close();
+ }
+ } catch (FileNotFoundException e) {
+ return null;
+ } catch (IOException e) {
+ throw new IOException("Error reading " + path, e);
+ }
+ }
+
+ /** Read a UTF-8 text file from the local drive. */
+ public static String readFile(final File parentDir, final String name)
+ throws IOException {
+ if (parentDir == null) {
+ return null;
+ }
+ final File path = new File(parentDir, name);
+ try {
+ return asString(new FileInputStream(path));
+ } catch (FileNotFoundException e) {
+ return null;
+ } catch (IOException e) {
+ throw new IOException("Error reading " + path, e);
+ }
+ }
+
+ private static String asString(final InputStream in)
+ throws UnsupportedEncodingException, IOException {
+ try {
+ final StringBuilder w = new StringBuilder();
+ final InputStreamReader r = new InputStreamReader(in, ENC);
+ final char[] buf = new char[512];
+ int n;
+ while ((n = r.read(buf)) > 0) {
+ w.append(buf, 0, n);
+ }
+ return w.toString();
+ } finally {
+ in.close();
+ }
+ }
+
+ private static DocumentBuilder newBuilder()
+ throws ParserConfigurationException {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(false);
+ factory.setExpandEntityReferences(false);
+ factory.setIgnoringComments(true);
+ final DocumentBuilder parser = factory.newDocumentBuilder();
+ return parser;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCanonicalWebUrlProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCanonicalWebUrlProvider.java
new file mode 100644
index 0000000000..a28abfe485
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCanonicalWebUrlProvider.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.CanonicalWebUrlProvider;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+import org.eclipse.jgit.lib.Config;
+
+import javax.servlet.http.HttpServletRequest;
+
+/** Sets {@link CanonicalWebUrl} to current HTTP request if not configured. */
+class HttpCanonicalWebUrlProvider extends CanonicalWebUrlProvider {
+ private Provider<HttpServletRequest> requestProvider;
+
+ @Inject
+ HttpCanonicalWebUrlProvider(@GerritServerConfig final Config config) {
+ super(config);
+ }
+
+ @Inject(optional = true)
+ void setHttpServletRequest(final Provider<HttpServletRequest> hsr) {
+ requestProvider = hsr;
+ }
+
+ @Override
+ public String get() {
+ String canonicalUrl = super.get();
+ if (canonicalUrl != null) {
+ return canonicalUrl;
+ }
+
+ if (requestProvider != null) {
+ // No canonical URL configured? Maybe we can get a reasonable
+ // guess from the incoming HTTP request, if we are currently
+ // inside of an HTTP request scope.
+ //
+ final HttpServletRequest req;
+ try {
+ req = requestProvider.get();
+ } catch (ProvisionException noWeb) {
+ if (noWeb.getCause() instanceof OutOfScopeException) {
+ // We can't obtain the request as we are not inside of
+ // an HTTP request scope. Callers must handle null.
+ //
+ return null;
+ } else {
+ throw noWeb;
+ }
+ }
+
+ final StringBuffer url = req.getRequestURL();
+ url.setLength(url.length() - req.getServletPath().length());
+ if (url.charAt(url.length() - 1) != '/') {
+ url.append('/');
+ }
+ return url.toString();
+ }
+
+ // We have no way of guessing our HTTP url.
+ //
+ return null;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCurrentUserProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCurrentUserProvider.java
new file mode 100644
index 0000000000..04de4087e2
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpCurrentUserProvider.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.servlet.RequestScoped;
+
+@RequestScoped
+class HttpCurrentUserProvider implements Provider<CurrentUser> {
+ private final WebSession session;
+
+ @Inject
+ HttpCurrentUserProvider(final WebSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public CurrentUser get() {
+ return session.getCurrentUser();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpIdentifiedUserProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpIdentifiedUserProvider.java
new file mode 100644
index 0000000000..d611098fe1
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpIdentifiedUserProvider.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.servlet.RequestScoped;
+
+@RequestScoped
+class HttpIdentifiedUserProvider implements Provider<IdentifiedUser> {
+ private final CurrentUser user;
+
+ @Inject
+ HttpIdentifiedUserProvider(final CurrentUser u) {
+ user = u;
+ }
+
+ @Override
+ public IdentifiedUser get() {
+ if (user instanceof IdentifiedUser) {
+ return (IdentifiedUser) user;
+ }
+ throw new ProvisionException(NotSignedInException.MESSAGE,
+ new NotSignedInException());
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java
new file mode 100644
index 0000000000..13b8293901
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpLogoutServlet.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+class HttpLogoutServlet extends HttpServlet {
+ private final Provider<WebSession> webSession;
+ private final Provider<String> urlProvider;
+ private final String logoutUrl;
+
+ @Inject
+ HttpLogoutServlet(final AuthConfig authConfig,
+ final Provider<WebSession> webSession,
+ @CanonicalWebUrl @Nullable final Provider<String> urlProvider,
+ final AccountManager accountManager) {
+ this.webSession = webSession;
+ this.urlProvider = urlProvider;
+ this.logoutUrl = authConfig.getLogoutURL();
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ webSession.get().logout();
+ if (logoutUrl != null) {
+ rsp.sendRedirect(logoutUrl);
+ } else {
+ rsp.sendRedirect(urlProvider.get());
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRemotePeerProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRemotePeerProvider.java
new file mode 100644
index 0000000000..9e0b0421e4
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HttpRemotePeerProvider.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.servlet.RequestScoped;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RequestScoped
+class HttpRemotePeerProvider implements Provider<SocketAddress> {
+ private final HttpServletRequest req;
+
+ @Inject
+ HttpRemotePeerProvider(final HttpServletRequest r) {
+ req = r;
+ }
+
+ @Override
+ public SocketAddress get() {
+ final String addr = req.getRemoteAddr();
+ final int port = req.getRemotePort();
+ try {
+ return new InetSocketAddress(InetAddress.getByName(addr), port);
+ } catch (UnknownHostException e) {
+ throw new ProvisionException("Cannot get @RemotePeer", e);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestCleanupFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestCleanupFilter.java
new file mode 100644
index 0000000000..0e6a567598
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestCleanupFilter.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import com.google.gerrit.server.RequestCleanup;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+/** Executes any pending {@link RequestCleanup} at the end of a request. */
+@Singleton
+class RequestCleanupFilter implements Filter {
+ private final Provider<RequestCleanup> cleanup;
+
+ @Inject
+ RequestCleanupFilter(final Provider<RequestCleanup> r) {
+ cleanup = r;
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public void doFilter(final ServletRequest request,
+ final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException {
+ try {
+ chain.doFilter(request, response);
+ } finally {
+ cleanup.get().run();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
new file mode 100644
index 0000000000..bb4e7d4b0c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -0,0 +1,132 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.httpd.raw.CatServlet;
+import com.google.gerrit.httpd.raw.HostPageServlet;
+import com.google.gerrit.httpd.raw.LegacyGerritServlet;
+import com.google.gerrit.httpd.raw.PrettifyServlet;
+import com.google.gerrit.httpd.raw.SshInfoServlet;
+import com.google.gerrit.httpd.raw.StaticServlet;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gwtexpui.server.CacheControlFilter;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.internal.UniqueAnnotations;
+import com.google.inject.servlet.ServletModule;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+class UrlModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ filter("/*").through(Key.get(CacheControlFilter.class));
+ bind(Key.get(CacheControlFilter.class)).in(SINGLETON);
+
+ serve("/").with(HostPageServlet.class);
+ serve("/Gerrit").with(LegacyGerritServlet.class);
+ serve("/Gerrit/*").with(legacyGerritScreen());
+ serve("/cat/*").with(CatServlet.class);
+ serve("/logout").with(HttpLogoutServlet.class);
+ serve("/prettify/*").with(PrettifyServlet.class);
+ serve("/signout").with(HttpLogoutServlet.class);
+ serve("/ssh_info").with(SshInfoServlet.class);
+ serve("/static/*").with(StaticServlet.class);
+
+ serve("/Main.class").with(notFound());
+ serve("/com/google/gerrit/main/*").with(notFound());
+
+ serve("/all").with(screen(PageLinks.ALL_MERGED));
+ serve("/mine").with(screen(PageLinks.MINE));
+ serve("/open").with(screen(PageLinks.ALL_OPEN));
+ serve("/settings").with(screen(PageLinks.SETTINGS));
+ serve("/starred").with(screen(PageLinks.MINE_STARRED));
+
+ serveRegex( //
+ "^/([1-9][0-9]*)/?$", //
+ "^/r/([0-9a-fA-F]{4," + RevId.LEN + "})/?$" //
+ ).with(changeQuery());
+ }
+
+ private Key<HttpServlet> notFound() {
+ return key(new HttpServlet() {
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ });
+ }
+
+ private Key<HttpServlet> screen(final String target) {
+ return key(new HttpServlet() {
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ toGerrit(target, req, rsp);
+ }
+ });
+ }
+
+ private Key<HttpServlet> legacyGerritScreen() {
+ return key(new HttpServlet() {
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final String token = req.getPathInfo().substring(1);
+ toGerrit(token, req, rsp);
+ }
+ });
+ }
+
+ private Key<HttpServlet> changeQuery() {
+ return key(new HttpServlet() {
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ toGerrit(PageLinks.toChangeQuery(req.getPathInfo()), req, rsp);
+ }
+ });
+ }
+
+ private Key<HttpServlet> key(final HttpServlet servlet) {
+ final Key<HttpServlet> srv =
+ Key.get(HttpServlet.class, UniqueAnnotations.create());
+ bind(srv).toProvider(new Provider<HttpServlet>() {
+ @Override
+ public HttpServlet get() {
+ return servlet;
+ }
+ }).in(SINGLETON);
+ return srv;
+ }
+
+ private void toGerrit(final String target, final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final StringBuilder url = new StringBuilder();
+ url.append(req.getContextPath());
+ url.append('/');
+ url.append('#');
+ url.append(target);
+ rsp.sendRedirect(url.toString());
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
new file mode 100644
index 0000000000..f11d35c3ae
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.httpd.auth.become.BecomeAnyAccountLoginServlet;
+import com.google.gerrit.httpd.auth.container.HttpAuthModule;
+import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
+import com.google.gerrit.httpd.auth.openid.OpenIdModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RemotePeer;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.config.GerritRequestModule;
+import com.google.gerrit.server.contact.ContactStore;
+import com.google.gerrit.server.contact.ContactStoreProvider;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.servlet.RequestScoped;
+import com.google.inject.servlet.ServletModule;
+
+import java.net.SocketAddress;
+
+class WebModule extends FactoryModule {
+ private final Provider<SshInfo> sshInfoProvider;
+ private final Provider<SshKeyCache> sshKeyCacheProvider;
+ private final AuthType loginType;
+
+ @Inject
+ WebModule(final Provider<SshInfo> sshInfoProvider,
+ final Provider<SshKeyCache> sshKeyCacheProvider,
+ final AuthConfig authConfig) {
+ this(sshInfoProvider, sshKeyCacheProvider, authConfig.getAuthType());
+ }
+
+ WebModule(final Provider<SshInfo> sshInfoProvider,
+ final Provider<SshKeyCache> sshKeyCacheProvider, final AuthType loginType) {
+ this.sshInfoProvider = sshInfoProvider;
+ this.sshKeyCacheProvider = sshKeyCacheProvider;
+ this.loginType = loginType;
+ }
+
+ @Override
+ protected void configure() {
+ install(new ServletModule() {
+ @Override
+ protected void configureServlets() {
+ filter("/*").through(RequestCleanupFilter.class);
+ }
+ });
+
+ switch (loginType) {
+ case OPENID:
+ install(new OpenIdModule());
+ break;
+
+ case HTTP:
+ case HTTP_LDAP:
+ install(new HttpAuthModule());
+ break;
+
+ case LDAP:
+ install(new LdapAuthModule());
+ break;
+
+ case DEVELOPMENT_BECOME_ANY_ACCOUNT:
+ install(new ServletModule() {
+ @Override
+ protected void configureServlets() {
+ serve("/become").with(BecomeAnyAccountLoginServlet.class);
+ }
+ });
+ break;
+
+ default:
+ throw new ProvisionException("Unsupported loginType: " + loginType);
+ }
+
+ install(new UrlModule());
+ install(new UiRpcModule());
+ install(new GerritRequestModule());
+
+ bind(SshInfo.class).toProvider(sshInfoProvider);
+ bind(SshKeyCache.class).toProvider(sshKeyCacheProvider);
+
+ bind(ContactStore.class).toProvider(ContactStoreProvider.class).in(
+ SINGLETON);
+ bind(GerritConfig.class).toProvider(GerritConfigProvider.class).in(
+ SINGLETON);
+ bind(AccountManager.class).in(SINGLETON);
+ bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
+ HttpRemotePeerProvider.class).in(RequestScoped.class);
+
+ install(WebSession.module());
+
+ bind(CurrentUser.class).toProvider(HttpCurrentUserProvider.class).in(
+ RequestScoped.class);
+ bind(IdentifiedUser.class).toProvider(HttpIdentifiedUserProvider.class).in(
+ RequestScoped.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSession.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSession.java
new file mode 100644
index 0000000000..b6f3c12339
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSession.java
@@ -0,0 +1,177 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import static java.util.concurrent.TimeUnit.HOURS;
+
+import com.google.gerrit.httpd.WebSessionManager.Key;
+import com.google.gerrit.httpd.WebSessionManager.Val;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.AccessPath;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.EvictionPolicy;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.google.inject.servlet.RequestScoped;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequestScoped
+public final class WebSession {
+ private static final String ACCOUNT_COOKIE = "GerritAccount";
+
+ static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final String cacheName = WebSessionManager.CACHE_NAME;
+ final TypeLiteral<Cache<Key, Val>> type =
+ new TypeLiteral<Cache<Key, Val>>() {};
+ disk(type, cacheName) //
+ .memoryLimit(1024) // reasonable default for many sites
+ .maxAge(12, HOURS) // expire sessions if they are inactive
+ .evictionPolicy(EvictionPolicy.LRU) // keep most recently used
+ ;
+ bind(WebSessionManager.class);
+ bind(WebSession.class).in(RequestScoped.class);
+ }
+ };
+ }
+
+ private final HttpServletRequest request;
+ private final HttpServletResponse response;
+ private final WebSessionManager manager;
+ private final AnonymousUser anonymous;
+ private final IdentifiedUser.RequestFactory identified;
+ private Cookie outCookie;
+
+ private Key key;
+ private Val val;
+
+ @Inject
+ WebSession(final HttpServletRequest request,
+ final HttpServletResponse response, final WebSessionManager manager,
+ final AnonymousUser anonymous,
+ final IdentifiedUser.RequestFactory identified) {
+ this.request = request;
+ this.response = response;
+ this.manager = manager;
+ this.anonymous = anonymous;
+ this.identified = identified;
+
+ final String cookie = readCookie();
+ if (cookie != null) {
+ key = new Key(cookie);
+ val = manager.get(key);
+ } else {
+ key = null;
+ val = null;
+ }
+
+ if (isSignedIn() && val.needsCookieRefresh()) {
+ // Cookie is more than half old. Send the cookie again to the
+ // client with an updated expiration date. We don't dare to
+ // change the key token here because there may be other RPCs
+ // queued up in the browser whose xsrfKey would not get updated
+ // with the new token, causing them to fail.
+ //
+ val = manager.createVal(key, val);
+ saveCookie();
+ }
+ }
+
+ private String readCookie() {
+ final Cookie[] all = request.getCookies();
+ if (all != null) {
+ for (final Cookie c : all) {
+ if (ACCOUNT_COOKIE.equals(c.getName())) {
+ final String v = c.getValue();
+ return v != null && !"".equals(v) ? v : null;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean isSignedIn() {
+ return val != null;
+ }
+
+ String getToken() {
+ return isSignedIn() ? key.getToken() : null;
+ }
+
+ public boolean isTokenValid(final String inputToken) {
+ return isSignedIn() && key.getToken().equals(inputToken);
+ }
+
+ CurrentUser getCurrentUser() {
+ if (isSignedIn()) {
+ return identified.create(AccessPath.WEB, val.getAccountId());
+ }
+ return anonymous;
+ }
+
+ public void login(final Account.Id id, final boolean rememberMe) {
+ logout();
+
+ key = manager.createKey(id);
+ val = manager.createVal(key, id, rememberMe);
+ saveCookie();
+ }
+
+ public void logout() {
+ if (val != null) {
+ manager.destroy(key);
+ key = null;
+ val = null;
+ saveCookie();
+ }
+ }
+
+ private void saveCookie() {
+ final String token;
+ final int ageSeconds;
+
+ if (key == null) {
+ token = "";
+ ageSeconds = 0 /* erase at client */;
+ } else {
+ token = key.getToken();
+ ageSeconds = manager.getCookieAge(val);
+ }
+
+ if (outCookie == null) {
+ String path = request.getContextPath();
+ if (path.equals("")) {
+ path = "/";
+ }
+ outCookie = new Cookie(ACCOUNT_COOKIE, token);
+ outCookie.setPath(path);
+ outCookie.setMaxAge(ageSeconds);
+ response.addCookie(outCookie);
+ } else {
+ outCookie.setValue(token);
+ outCookie.setMaxAge(ageSeconds);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
new file mode 100644
index 0000000000..aaa254fe76
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java
@@ -0,0 +1,212 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
+import static java.util.concurrent.TimeUnit.HOURS;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.cache.Cache;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.security.SecureRandom;
+
+@Singleton
+class WebSessionManager {
+ static final String CACHE_NAME = "web_sessions";
+
+ static long now() {
+ return System.currentTimeMillis();
+ }
+
+ private final SecureRandom prng;
+ private final Cache<Key, Val> self;
+
+ @Inject
+ WebSessionManager(@Named(CACHE_NAME) final Cache<Key, Val> cache) {
+ prng = new SecureRandom();
+ self = cache;
+ }
+
+ Key createKey(final Account.Id who) {
+ try {
+ final int nonceLen = 20;
+ final ByteArrayOutputStream buf;
+ final byte[] rnd = new byte[nonceLen];
+ prng.nextBytes(rnd);
+
+ buf = new ByteArrayOutputStream(3 + nonceLen);
+ writeVarInt32(buf, (int) Key.serialVersionUID);
+ writeVarInt32(buf, who.get());
+ writeBytes(buf, rnd);
+
+ return new Key(CookieBase64.encode(buf.toByteArray()));
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot produce new account cookie", e);
+ }
+ }
+
+ Val createVal(final Key key, final Val val) {
+ return createVal(key, val.getAccountId(), val.isPersistentCookie());
+ }
+
+ Val createVal(final Key key, final Account.Id who, final boolean remember) {
+ // Refresh the cookie every hour or when it is half-expired.
+ // This reduces the odds that the user session will be kicked
+ // early but also avoids us needing to refresh the cookie on
+ // every single request.
+ //
+ final long halfAgeRefresh = self.getTimeToLive(MILLISECONDS) >>> 1;
+ final long minRefresh = MILLISECONDS.convert(1, HOURS);
+ final long refresh = Math.min(halfAgeRefresh, minRefresh);
+ final long refreshCookieAt = now() + refresh;
+
+ final Val val = new Val(who, refreshCookieAt, remember);
+ self.put(key, val);
+ return val;
+ }
+
+ int getCookieAge(final Val val) {
+ if (val.isPersistentCookie()) {
+ // Client may store the cookie until we would remove it from our
+ // own cache, after which it will certainly be invalid.
+ //
+ return (int) self.getTimeToLive(SECONDS);
+ } else {
+ // Client should not store the cookie, as the user asked for us
+ // to not remember them long-term. Sending -1 as the age will
+ // cause the cookie to be only for this "browser session", which
+ // is usually until the user exits their browser.
+ //
+ return -1;
+ }
+ }
+
+ Val get(final Key key) {
+ return self.get(key);
+ }
+
+ void destroy(final Key key) {
+ self.remove(key);
+ }
+
+ static final class Key implements Serializable {
+ static final long serialVersionUID = 2L;
+
+ private transient String token;
+
+ Key(final String t) {
+ token = t;
+ }
+
+ String getToken() {
+ return token;
+ }
+
+ @Override
+ public int hashCode() {
+ return token.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Key && token.equals(((Key) obj).token);
+ }
+
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ writeString(out, token);
+ }
+
+ private void readObject(final ObjectInputStream in) throws IOException {
+ token = readString(in);
+ }
+ }
+
+ static final class Val implements Serializable {
+ static final long serialVersionUID = Key.serialVersionUID;
+
+ private transient Account.Id accountId;
+ private transient long refreshCookieAt;
+ private transient boolean persistentCookie;
+
+ Val(final Account.Id accountId, final long refreshCookieAt,
+ final boolean persistentCookie) {
+ this.accountId = accountId;
+ this.refreshCookieAt = refreshCookieAt;
+ this.persistentCookie = persistentCookie;
+ }
+
+ Account.Id getAccountId() {
+ return accountId;
+ }
+
+ boolean needsCookieRefresh() {
+ return refreshCookieAt <= now();
+ }
+
+ boolean isPersistentCookie() {
+ return persistentCookie;
+ }
+
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ writeVarInt32(out, 1);
+ writeVarInt32(out, accountId.get());
+
+ writeVarInt32(out, 2);
+ writeFixInt64(out, refreshCookieAt);
+
+ writeVarInt32(out, 3);
+ writeVarInt32(out, persistentCookie ? 1 : 0);
+
+ writeVarInt32(out, 0);
+ }
+
+ private void readObject(final ObjectInputStream in) throws IOException {
+ PARSE: for (;;) {
+ final int tag = readVarInt32(in);
+ switch (tag) {
+ case 0:
+ break PARSE;
+ case 1:
+ accountId = new Account.Id(readVarInt32(in));
+ continue;
+ case 2:
+ refreshCookieAt = readFixInt64(in);
+ continue;
+ case 3:
+ persistentCookie = readVarInt32(in) != 0;
+ continue;
+ default:
+ throw new IOException("Unknown tag found in object: " + tag);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/webapp/WEB-INF/BecomeAnyAccount.html b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
index 2cd6a84824..2cd6a84824 100644
--- a/src/main/webapp/WEB-INF/BecomeAnyAccount.html
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccount.html
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
new file mode 100644
index 0000000000..d823db5fee
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -0,0 +1,175 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.become;
+
+import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+@Singleton
+public class BecomeAnyAccountLoginServlet extends HttpServlet {
+ private final SchemaFactory<ReviewDb> schema;
+ private final Provider<WebSession> webSession;
+ private final Provider<String> urlProvider;
+ private final byte[] raw;
+
+ @Inject
+ BecomeAnyAccountLoginServlet(final Provider<WebSession> ws,
+ final SchemaFactory<ReviewDb> sf,
+ final @CanonicalWebUrl @Nullable Provider<String> up,
+ final ServletContext servletContext) throws IOException {
+ webSession = ws;
+ schema = sf;
+ urlProvider = up;
+
+ final String pageName = "BecomeAnyAccount.html";
+ final String doc = HtmlDomUtil.readFile(getClass(), pageName);
+ if (doc == null) {
+ throw new FileNotFoundException("No " + pageName + " in webapp");
+ }
+
+ raw = doc.getBytes(HtmlDomUtil.ENC);
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ doPost(req, rsp);
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+
+ final List<Account> accounts;
+ if (req.getParameter("ssh_user_name") != null) {
+ accounts = bySshUserName(rsp, req.getParameter("ssh_user_name"));
+
+ } else if (req.getParameter("preferred_email") != null) {
+ accounts = byPreferredEmail(rsp, req.getParameter("preferred_email"));
+
+ } else if (req.getParameter("account_id") != null) {
+ accounts = byAccountId(rsp, req.getParameter("account_id"));
+
+ } else {
+ rsp.setContentType("text/html");
+ rsp.setCharacterEncoding(HtmlDomUtil.ENC);
+ rsp.setContentLength(raw.length);
+ final OutputStream out = rsp.getOutputStream();
+ try {
+ out.write(raw);
+ } finally {
+ out.close();
+ }
+ return;
+ }
+
+ if (accounts.size() == 1) {
+ final Account account = accounts.get(0);
+ webSession.get().login(account.getId(), false);
+ rsp.sendRedirect(urlProvider.get());
+
+ } else {
+ rsp.setContentType("text/html");
+ rsp.setCharacterEncoding(HtmlDomUtil.ENC);
+ final Writer out = rsp.getWriter();
+ out.write("<html>");
+ out.write("<body>");
+ out.write("<h1>Account Not Found</h1>");
+ out.write("</body>");
+ out.write("</html>");
+ out.close();
+ }
+ }
+
+ private List<Account> bySshUserName(final HttpServletResponse rsp,
+ final String userName) {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final Account account = db.accounts().bySshUserName(userName);
+ return account != null ? Collections.<Account> singletonList(account)
+ : Collections.<Account> emptyList();
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ getServletContext().log("cannot query database", e);
+ return Collections.<Account> emptyList();
+ }
+ }
+
+ private List<Account> byPreferredEmail(final HttpServletResponse rsp,
+ final String email) {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ return db.accounts().byPreferredEmail(email).toList();
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ getServletContext().log("cannot query database", e);
+ return Collections.<Account> emptyList();
+ }
+ }
+
+ private List<Account> byAccountId(final HttpServletResponse rsp,
+ final String idStr) {
+ final Account.Id id;
+ try {
+ id = Account.Id.parse(idStr);
+ } catch (NumberFormatException nfe) {
+ return Collections.<Account> emptyList();
+ }
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final Account account = db.accounts().get(id);
+ return account != null ? Collections.<Account> singletonList(account)
+ : Collections.<Account> emptyList();
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ getServletContext().log("cannot query database", e);
+ return Collections.<Account> emptyList();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
new file mode 100644
index 0000000000..1afd72de01
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthFilter.java
@@ -0,0 +1,116 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.container;
+
+import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.httpd.raw.HostPageServlet;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Watches request for the host page and requires login if not yet signed in.
+ * <p>
+ * If HTTP authentication has been enabled on this server this filter is bound
+ * in front of the {@link HostPageServlet} and redirects users who are not yet
+ * signed in to visit {@code /login/}, so the web container can force login.
+ * This redirect is performed with JavaScript, such that any existing anchor
+ * token in the URL can be rewritten and preserved through the authentication
+ * process of any enterprise single sign-on solutions.
+ */
+@Singleton
+class HttpAuthFilter implements Filter {
+ private final Provider<WebSession> webSession;
+ private final byte[] signInRaw;
+ private final byte[] signInGzip;
+
+ @Inject
+ HttpAuthFilter(final Provider<WebSession> webSession,
+ final ServletContext servletContext) throws IOException {
+ this.webSession = webSession;
+
+ final String pageName = "LoginRedirect.html";
+ final String doc = HtmlDomUtil.readFile(getClass(), pageName);
+ if (doc == null) {
+ throw new FileNotFoundException("No " + pageName + " in webapp");
+ }
+
+ signInRaw = doc.getBytes(HtmlDomUtil.ENC);
+ signInGzip = HtmlDomUtil.compress(signInRaw);
+ }
+
+ @Override
+ public void doFilter(final ServletRequest request,
+ final ServletResponse response, final FilterChain chain)
+ throws IOException, ServletException {
+ if (!webSession.get().isSignedIn()) {
+ // Not signed in yet. Since the browser state might have an anchor
+ // token which we want to capture and carry through the auth process
+ // we send back JavaScript now to capture that, and do the real work
+ // of redirecting to the authentication area.
+ //
+ final HttpServletRequest req = (HttpServletRequest) request;
+ final HttpServletResponse rsp = (HttpServletResponse) response;
+ final byte[] tosend;
+ if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ rsp.setHeader("Content-Encoding", "gzip");
+ tosend = signInGzip;
+ } else {
+ tosend = signInRaw;
+ }
+
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.setContentType("text/html");
+ rsp.setCharacterEncoding(HtmlDomUtil.ENC);
+ rsp.setContentLength(tosend.length);
+ final OutputStream out = rsp.getOutputStream();
+ try {
+ out.write(tosend);
+ } finally {
+ out.close();
+ }
+ } else {
+ // Already signed in, forward the request.
+ //
+ chain.doFilter(request, response);
+ }
+ }
+
+ @Override
+ public void init(final FilterConfig filterConfig) {
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthModule.java
new file mode 100644
index 0000000000..553b1f4217
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpAuthModule.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.container;
+
+import com.google.inject.servlet.ServletModule;
+
+/** Servlets and support related to HTTP authentication. */
+public class HttpAuthModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ filter("/").through(HttpAuthFilter.class);
+ serve("/login/*").with(HttpLoginServlet.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
new file mode 100644
index 0000000000..8ea4f2f132
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/HttpLoginServlet.java
@@ -0,0 +1,171 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.container;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.AuthResult;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Initializes the user session if HTTP authentication is enabled.
+ * <p>
+ * If HTTP authentication has been enabled this servlet binds to {@code /login/}
+ * and initializes the user session based on user information contained in the
+ * HTTP request.
+ */
+@Singleton
+class HttpLoginServlet extends HttpServlet {
+ private static final Logger log =
+ LoggerFactory.getLogger(HttpLoginServlet.class);
+
+ private static final String AUTHORIZATION = "Authorization";
+ private final Provider<WebSession> webSession;
+ private final Provider<String> urlProvider;
+ private final AccountManager accountManager;
+ private final String loginHeader;
+
+ @Inject
+ HttpLoginServlet(final AuthConfig authConfig,
+ final Provider<WebSession> webSession,
+ @CanonicalWebUrl @Nullable final Provider<String> urlProvider,
+ final AccountManager accountManager) {
+ this.webSession = webSession;
+ this.urlProvider = urlProvider;
+ this.accountManager = accountManager;
+
+ final String hdr = authConfig.getLoginHttpHeader();
+ this.loginHeader = hdr != null && !hdr.equals("") ? hdr : AUTHORIZATION;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws ServletException, IOException {
+ final String token = getToken(req);
+ if ("logout".equals(token) || "signout".equals(token)) {
+ req.getRequestDispatcher("/logout").forward(req, rsp);
+ return;
+ }
+
+ final String user = getRemoteUser(req);
+ if (user == null || "".equals(user)) {
+ log.error("Unable to authenticate user by " + loginHeader
+ + " request header. Check container or server configuration.");
+ rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ final AuthRequest areq = AuthRequest.forUser(user);
+ final AuthResult arsp;
+ try {
+ arsp = accountManager.authenticate(areq);
+ } catch (AccountException e) {
+ log.error("Unable to authenticate user \"" + user + "\"", e);
+ rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ final StringBuilder rdr = new StringBuilder();
+ rdr.append(urlProvider.get());
+ rdr.append('#');
+ if (arsp.isNew() && !token.startsWith(PageLinks.REGISTER + ",")) {
+ rdr.append(PageLinks.REGISTER);
+ rdr.append(',');
+ }
+ rdr.append(token);
+
+ webSession.get().login(arsp.getAccountId(), false);
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.sendRedirect(rdr.toString());
+ }
+
+ private String getToken(final HttpServletRequest req) {
+ String token = req.getPathInfo();
+ if (token != null && token.startsWith("/")) {
+ token = token.substring(1);
+ }
+ if (token == null || token.isEmpty()) {
+ token = PageLinks.MINE;
+ }
+ return token;
+ }
+
+ private String getRemoteUser(final HttpServletRequest req) {
+ if (AUTHORIZATION.equals(loginHeader)) {
+ final String user = req.getRemoteUser();
+ if (user != null && !"".equals(user)) {
+ // The container performed the authentication, and has the user
+ // identity already decoded for us. Honor that as we have been
+ // configured to honor HTTP authentication.
+ //
+ return user;
+ }
+
+ // If the container didn't do the authentication we might
+ // have done it in the front-end web server. Try to split
+ // the identity out of the Authorization header and honor it.
+ //
+ String auth = req.getHeader(AUTHORIZATION);
+ if (auth == null || "".equals(auth)) {
+ return null;
+
+ } else if (auth.startsWith("Basic ")) {
+ auth = auth.substring("Basic ".length());
+ auth = new String(Base64.decode(auth));
+ final int c = auth.indexOf(':');
+ return c > 0 ? auth.substring(0, c) : null;
+
+ } else if (auth.startsWith("Digest ")) {
+ final int u = auth.indexOf("username=\"");
+ if (u <= 0) {
+ return null;
+ }
+ auth = auth.substring(u + 10);
+ final int e = auth.indexOf('"');
+ return e > 0 ? auth.substring(0, auth.indexOf('"')) : null;
+
+ } else {
+ return null;
+ }
+ } else {
+ // Nonstandard HTTP header. We have been told to trust this
+ // header blindly as-is.
+ //
+ final String user = req.getHeader(loginHeader);
+ return user != null && !"".equals(user) ? user : null;
+ }
+ }
+}
diff --git a/src/main/webapp/WEB-INF/LoginRedirect.html b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/LoginRedirect.html
index 856ee71f3c..856ee71f3c 100644
--- a/src/main/webapp/WEB-INF/LoginRedirect.html
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/container/LoginRedirect.html
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LdapAuthModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LdapAuthModule.java
new file mode 100644
index 0000000000..c46edc3808
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LdapAuthModule.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.ldap;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.inject.servlet.ServletModule;
+
+/** RPC support related to username/password LDAP authentication. */
+public class LdapAuthModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ serve("/login/*").with(LoginRedirectServlet.class);
+ install(new RpcServletModule(UiRpcModule.PREFIX) {
+ @Override
+ protected void configureServlets() {
+ rpc(UserPassAuthServiceImpl.class);
+ }
+ });
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LoginRedirectServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LoginRedirectServlet.java
new file mode 100644
index 0000000000..0952d87e2c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/LoginRedirectServlet.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.ldap;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+class LoginRedirectServlet extends HttpServlet {
+ private final Provider<WebSession> webSession;
+ private final Provider<String> urlProvider;
+
+ @Inject
+ LoginRedirectServlet(final Provider<WebSession> webSession,
+ @CanonicalWebUrl @Nullable final Provider<String> urlProvider) {
+ this.webSession = webSession;
+ this.urlProvider = urlProvider;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final String token;
+ if (webSession.get().isSignedIn()) {
+ token = getToken(req);
+ } else {
+ final String msg = "Session cookie not available.";
+ token = "SignInFailure," + SignInMode.SIGN_IN + "," + msg;
+ }
+
+ final StringBuilder rdr = new StringBuilder();
+ rdr.append(urlProvider.get());
+ rdr.append('#');
+ rdr.append(token);
+
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.sendRedirect(rdr.toString());
+ }
+
+ private String getToken(final HttpServletRequest req) {
+ String token = req.getPathInfo();
+ if (token != null && token.startsWith("/")) {
+ token = token.substring(1);
+ }
+ if (token == null || token.isEmpty()) {
+ token = PageLinks.MINE;
+ }
+ return token;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/UserPassAuthServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/UserPassAuthServiceImpl.java
new file mode 100644
index 0000000000..afe7ea6e4d
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/ldap/UserPassAuthServiceImpl.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.ldap;
+
+import com.google.gerrit.common.auth.userpass.LoginResult;
+import com.google.gerrit.common.auth.userpass.UserPassAuthService;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.AuthResult;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+class UserPassAuthServiceImpl implements UserPassAuthService {
+ private final Provider<WebSession> webSession;
+ private final AccountManager accountManager;
+
+ @Inject
+ UserPassAuthServiceImpl(final Provider<WebSession> webSession,
+ final AccountManager accountManager) {
+ this.webSession = webSession;
+ this.accountManager = accountManager;
+ }
+
+ @Override
+ public void authenticate(final String username, final String password,
+ final AsyncCallback<LoginResult> callback) {
+ LoginResult result = new LoginResult();
+ if (username == null || "".equals(username) //
+ || password == null || "".equals(password)) {
+ result.success = false;
+ callback.onSuccess(result);
+ return;
+ }
+
+ final AuthRequest req = AuthRequest.forUser(username);
+ req.setPassword(password);
+
+ final AuthResult res;
+ try {
+ res = accountManager.authenticate(req);
+ } catch (AccountException e) {
+ result.success = false;
+ callback.onSuccess(result);
+ return;
+ }
+
+ result.success = true;
+ result.isNew = res.isNew();
+ webSession.get().login(res.getAccountId(), false);
+ callback.onSuccess(result);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java
new file mode 100644
index 0000000000..cfb767d0ba
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdLoginServlet.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.openid;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** Handles the <code>/OpenID</code> URL for web based single-sign-on. */
+@SuppressWarnings("serial")
+@Singleton
+class OpenIdLoginServlet extends HttpServlet {
+ private final OpenIdServiceImpl impl;
+
+ @Inject
+ OpenIdLoginServlet(final OpenIdServiceImpl i) {
+ impl = i;
+ }
+
+ @Override
+ public void doGet(final HttpServletRequest req, final HttpServletResponse rsp)
+ throws IOException {
+ doPost(req, rsp);
+ }
+
+ @Override
+ public void doPost(final HttpServletRequest req, final HttpServletResponse rsp)
+ throws IOException {
+ try {
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ impl.doAuth(req, rsp);
+ } catch (Exception e) {
+ getServletContext().log("Unexpected error during authentication", e);
+ rsp.reset();
+ rsp.sendError(500);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java
new file mode 100644
index 0000000000..f6ef51a685
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdModule.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.openid;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.inject.TypeLiteral;
+import com.google.inject.servlet.ServletModule;
+
+import java.util.List;
+
+/** Servlets and RPC support related to OpenID authentication. */
+public class OpenIdModule extends ServletModule {
+ @Override
+ protected void configureServlets() {
+ install(new CacheModule() {
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<String, List>> type =
+ new TypeLiteral<Cache<String, List>>() {};
+ core(type, "openid") //
+ .maxAge(5, MINUTES) // don't cache too long, might be stale
+ .memoryLimit(64) // short TTL means we won't have many entries
+ ;
+ }
+ });
+
+ serve("/" + OpenIdServiceImpl.RETURN_URL).with(OpenIdLoginServlet.class);
+
+ install(new RpcServletModule(RpcServletModule.PREFIX) {
+ @Override
+ protected void configureServlets() {
+ rpc(OpenIdServiceImpl.class);
+ }
+ });
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
new file mode 100644
index 0000000000..ff2704ae91
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/openid/OpenIdServiceImpl.java
@@ -0,0 +1,467 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.auth.openid;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.auth.openid.DiscoveryResult;
+import com.google.gerrit.common.auth.openid.OpenIdService;
+import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.UrlEncoded;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.KeyUtil;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+import org.openid4java.consumer.ConsumerException;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.VerificationResult;
+import org.openid4java.discovery.DiscoveryException;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.message.AuthRequest;
+import org.openid4java.message.Message;
+import org.openid4java.message.MessageException;
+import org.openid4java.message.MessageExtension;
+import org.openid4java.message.ParameterList;
+import org.openid4java.message.ax.AxMessage;
+import org.openid4java.message.ax.FetchRequest;
+import org.openid4java.message.ax.FetchResponse;
+import org.openid4java.message.sreg.SRegMessage;
+import org.openid4java.message.sreg.SRegRequest;
+import org.openid4java.message.sreg.SRegResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+class OpenIdServiceImpl implements OpenIdService {
+ private static final Logger log =
+ LoggerFactory.getLogger(OpenIdServiceImpl.class);
+
+ static final String RETURN_URL = "OpenID";
+
+ private static final String P_MODE = "gerrit.mode";
+ private static final String P_TOKEN = "gerrit.token";
+ private static final String P_REMEMBER = "gerrit.remember";
+ private static final String P_CLAIMED = "gerrit.claimed";
+ private static final int LASTID_AGE = 365 * 24 * 60 * 60; // seconds
+
+ private static final String OPENID_MODE = "openid.mode";
+ private static final String OMODE_CANCEL = "cancel";
+
+ private static final String SCHEMA_EMAIL =
+ "http://schema.openid.net/contact/email";
+ private static final String SCHEMA_FIRSTNAME =
+ "http://schema.openid.net/namePerson/first";
+ private static final String SCHEMA_LASTNAME =
+ "http://schema.openid.net/namePerson/last";
+
+ private final Provider<WebSession> webSession;
+ private final Provider<IdentifiedUser> identifiedUser;
+ private final Provider<String> urlProvider;
+ private final AccountManager accountManager;
+ private final ConsumerManager manager;
+ private final SelfPopulatingCache<String, List> discoveryCache;
+
+ @Inject
+ OpenIdServiceImpl(final Provider<WebSession> cf,
+ final Provider<IdentifiedUser> iu,
+ @CanonicalWebUrl @Nullable final Provider<String> up,
+ @Named("openid") final Cache<String, List> openidCache,
+ final AccountManager am) throws ConsumerException {
+ webSession = cf;
+ identifiedUser = iu;
+ urlProvider = up;
+ accountManager = am;
+ manager = new ConsumerManager();
+
+ discoveryCache = new SelfPopulatingCache<String, List>(openidCache) {
+ @Override
+ protected List createEntry(final String url) throws Exception {
+ try {
+ final List<?> list = manager.discover(url);
+ return list != null && !list.isEmpty() ? list : null;
+ } catch (DiscoveryException e) {
+ return null;
+ }
+ }
+ };
+ }
+
+ public void discover(final String openidIdentifier,
+ final SignInMode mode, final boolean remember,
+ final String returnToken, final AsyncCallback<DiscoveryResult> callback) {
+ final State state;
+ state = init(openidIdentifier, mode, remember, returnToken);
+ if (state == null) {
+ callback.onSuccess(new DiscoveryResult(false));
+ return;
+ }
+
+ final AuthRequest aReq;
+ try {
+ aReq = manager.authenticate(state.discovered, state.retTo.toString());
+ aReq.setRealm(state.contextUrl);
+
+ if (requestRegistration(aReq)) {
+ final SRegRequest sregReq = SRegRequest.createFetchRequest();
+ sregReq.addAttribute("fullname", true);
+ sregReq.addAttribute("email", true);
+ aReq.addExtension(sregReq);
+
+ final FetchRequest fetch = FetchRequest.createFetchRequest();
+ fetch.addAttribute("FirstName", SCHEMA_FIRSTNAME, true);
+ fetch.addAttribute("LastName", SCHEMA_LASTNAME, true);
+ fetch.addAttribute("Email", SCHEMA_EMAIL, true);
+ aReq.addExtension(fetch);
+ }
+ } catch (MessageException e) {
+ callback.onSuccess(new DiscoveryResult(false));
+ return;
+ } catch (ConsumerException e) {
+ callback.onSuccess(new DiscoveryResult(false));
+ return;
+ }
+
+ callback.onSuccess(new DiscoveryResult(true, aReq.getDestinationUrl(false),
+ aReq.getParameterMap()));
+ }
+
+ private boolean requestRegistration(final AuthRequest aReq) {
+ if (AuthRequest.SELECT_ID.equals(aReq.getIdentity())) {
+ // We don't know anything about the identity, as the provider
+ // will offer the user a way to indicate their identity. Skip
+ // any database query operation and assume we must ask for the
+ // registration information, in case the identity is new to us.
+ //
+ return true;
+
+ }
+
+ // We might already have this account on file. Look for it.
+ //
+ try {
+ return accountManager.lookup(aReq.getIdentity()) == null;
+ } catch (AccountException e) {
+ log.warn("Cannot determine if user account exists", e);
+ return true;
+ }
+ }
+
+ /** Called by {@link OpenIdLoginServlet} doGet, doPost */
+ void doAuth(final HttpServletRequest req, final HttpServletResponse rsp)
+ throws Exception {
+ if (OMODE_CANCEL.equals(req.getParameter(OPENID_MODE))) {
+ cancel(req, rsp);
+ return;
+ }
+
+ // Process the authentication response.
+ //
+ final SignInMode mode = signInMode(req);
+ final String openidIdentifier = req.getParameter("openid.identity");
+ final String claimedIdentifier = req.getParameter(P_CLAIMED);
+ final String returnToken = req.getParameter(P_TOKEN);
+ final boolean remember = "1".equals(req.getParameter(P_REMEMBER));
+ final String rediscoverIdentifier =
+ claimedIdentifier != null ? claimedIdentifier : openidIdentifier;
+ final State state;
+
+ state = init(rediscoverIdentifier, mode, remember, returnToken);
+ if (state == null) {
+ // Re-discovery must have failed, we can't run a login.
+ //
+ cancel(req, rsp);
+ return;
+ }
+
+ final String returnTo = req.getParameter("openid.return_to");
+ if (returnTo != null && returnTo.contains("openid.rpnonce=")) {
+ // Some providers (claimid.com) seem to embed these request
+ // parameters into our return_to URL, and then give us them
+ // in the return_to request parameter. But not all.
+ //
+ state.retTo.put("openid.rpnonce", req.getParameter("openid.rpnonce"));
+ state.retTo.put("openid.rpsig", req.getParameter("openid.rpsig"));
+ }
+
+ final VerificationResult result =
+ manager.verify(state.retTo.toString(), new ParameterList(req
+ .getParameterMap()), state.discovered);
+ if (result.getVerifiedId() == null /* authentication failure */) {
+ if ("Nonce verification failed.".equals(result.getStatusMsg())) {
+ // We might be suffering from clock skew on this system.
+ //
+ log.error("OpenID failure: " + result.getStatusMsg()
+ + " Likely caused by clock skew on this server,"
+ + " install/configure NTP.");
+ cancelWithError(req, rsp, result.getStatusMsg());
+
+ } else if (result.getStatusMsg() != null) {
+ // Authentication failed.
+ //
+ log.error("OpenID failure: " + result.getStatusMsg());
+ cancelWithError(req, rsp, result.getStatusMsg());
+
+ } else {
+ // Assume authentication was canceled.
+ //
+ cancel(req, rsp);
+ }
+ return;
+ }
+
+ final Message authRsp = result.getAuthResponse();
+ SRegResponse sregRsp = null;
+ FetchResponse fetchRsp = null;
+
+ if (authRsp.hasExtension(SRegMessage.OPENID_NS_SREG)) {
+ final MessageExtension ext =
+ authRsp.getExtension(SRegMessage.OPENID_NS_SREG);
+ if (ext instanceof SRegResponse) {
+ sregRsp = (SRegResponse) ext;
+ }
+ }
+
+ if (authRsp.hasExtension(AxMessage.OPENID_NS_AX)) {
+ final MessageExtension ext = authRsp.getExtension(AxMessage.OPENID_NS_AX);
+ if (ext instanceof FetchResponse) {
+ fetchRsp = (FetchResponse) ext;
+ }
+ }
+
+ final com.google.gerrit.server.account.AuthRequest areq =
+ new com.google.gerrit.server.account.AuthRequest(openidIdentifier);
+
+ if (sregRsp != null) {
+ areq.setDisplayName(sregRsp.getAttributeValue("fullname"));
+ areq.setEmailAddress(sregRsp.getAttributeValue("email"));
+
+ } else if (fetchRsp != null) {
+ final String firstName = fetchRsp.getAttributeValue("FirstName");
+ final String lastName = fetchRsp.getAttributeValue("LastName");
+ final StringBuilder n = new StringBuilder();
+ if (firstName != null && firstName.length() > 0) {
+ n.append(firstName);
+ }
+ if (lastName != null && lastName.length() > 0) {
+ if (n.length() > 0) {
+ n.append(' ');
+ }
+ n.append(lastName);
+ }
+ areq.setDisplayName(n.length() > 0 ? n.toString() : null);
+ areq.setEmailAddress(fetchRsp.getAttributeValue("Email"));
+ }
+
+ if (claimedIdentifier != null) {
+ // The user used a claimed identity which has delegated to the verified
+ // identity we have in our AuthRequest above. We still should have a
+ // link between the two, so set one up if not present.
+ //
+ Account.Id claimedId = accountManager.lookup(claimedIdentifier);
+ Account.Id actualId = accountManager.lookup(areq.getExternalId());
+
+ if (claimedId != null && actualId != null) {
+ if (claimedId.equals(actualId)) {
+ // Both link to the same account, that's what we expected.
+ } else {
+ // This is (for now) a fatal error. There are two records
+ // for what might be the same user.
+ //
+ log.error("OpenID accounts disagree over user identity:\n"
+ + " Claimed ID: " + claimedId + " is " + claimedIdentifier
+ + "\n" + " Delgate ID: " + actualId + " is "
+ + areq.getExternalId());
+ cancelWithError(req, rsp, "Contact site administrator");
+ return;
+ }
+
+ } else if (claimedId == null && actualId != null) {
+ // Older account, the actual was already created but the claimed
+ // was missing due to a bug in Gerrit. Link the claimed.
+ //
+ final com.google.gerrit.server.account.AuthRequest linkReq =
+ new com.google.gerrit.server.account.AuthRequest(claimedIdentifier);
+ linkReq.setDisplayName(areq.getDisplayName());
+ linkReq.setEmailAddress(areq.getEmailAddress());
+ accountManager.link(actualId, linkReq);
+
+ } else if (claimedId != null && actualId == null) {
+ // Claimed account already exists, but it smells like the user has
+ // changed their delegate to point to a different provider. Link
+ // the new provider.
+ //
+ accountManager.link(claimedId, areq);
+
+ } else {
+ // Both are null, we are going to create a new account below.
+ }
+ }
+
+ try {
+ switch (mode) {
+ case REGISTER:
+ case SIGN_IN:
+ final com.google.gerrit.server.account.AuthResult arsp;
+ arsp = accountManager.authenticate(areq);
+
+ final Cookie lastId = new Cookie(OpenIdUrls.LASTID_COOKIE, "");
+ lastId.setPath(req.getContextPath() + "/");
+ if (remember) {
+ lastId.setValue(rediscoverIdentifier);
+ lastId.setMaxAge(LASTID_AGE);
+ } else {
+ lastId.setMaxAge(0);
+ }
+ rsp.addCookie(lastId);
+ webSession.get().login(arsp.getAccountId(), remember);
+ if (arsp.isNew() && claimedIdentifier != null) {
+ final com.google.gerrit.server.account.AuthRequest linkReq =
+ new com.google.gerrit.server.account.AuthRequest(
+ claimedIdentifier);
+ linkReq.setDisplayName(areq.getDisplayName());
+ linkReq.setEmailAddress(areq.getEmailAddress());
+ accountManager.link(arsp.getAccountId(), linkReq);
+ }
+ callback(arsp.isNew(), req, rsp);
+ break;
+
+ case LINK_IDENTIY:
+ accountManager.link(identifiedUser.get().getAccountId(), areq);
+ callback(false, req, rsp);
+ break;
+ }
+ } catch (AccountException e) {
+ log.error("OpenID authentication failure", e);
+ cancelWithError(req, rsp, "Contact site administrator");
+ }
+ }
+
+ private boolean isSignIn(final SignInMode mode) {
+ switch (mode) {
+ case SIGN_IN:
+ case REGISTER:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private static SignInMode signInMode(final HttpServletRequest req) {
+ try {
+ return SignInMode.valueOf(req.getParameter(P_MODE));
+ } catch (RuntimeException e) {
+ return SignInMode.SIGN_IN;
+ }
+ }
+
+ private void callback(final boolean isNew, final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ String token = req.getParameter(P_TOKEN);
+ if (token == null || token.isEmpty() || token.startsWith("SignInFailure,")) {
+ token = PageLinks.MINE;
+ }
+
+ final StringBuilder rdr = new StringBuilder();
+ rdr.append(urlProvider.get());
+ rdr.append('#');
+ if (isNew && !token.startsWith(PageLinks.REGISTER + ",")) {
+ rdr.append(PageLinks.REGISTER);
+ rdr.append(',');
+ }
+ rdr.append(token);
+ rsp.sendRedirect(rdr.toString());
+ }
+
+ private void cancel(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ if (isSignIn(signInMode(req))) {
+ webSession.get().logout();
+ }
+ callback(false, req, rsp);
+ }
+
+ private void cancelWithError(final HttpServletRequest req,
+ final HttpServletResponse rsp, final String errorDetail)
+ throws IOException {
+ final SignInMode mode = signInMode(req);
+ if (isSignIn(mode)) {
+ webSession.get().logout();
+ }
+ final StringBuilder rdr = new StringBuilder();
+ rdr.append(urlProvider.get());
+ rdr.append('#');
+ rdr.append("SignInFailure");
+ rdr.append(',');
+ rdr.append(mode.name());
+ rdr.append(',');
+ rdr.append(errorDetail != null ? KeyUtil.encode(errorDetail) : "");
+ rsp.sendRedirect(rdr.toString());
+ }
+
+ private State init(final String openidIdentifier,
+ final SignInMode mode, final boolean remember,
+ final String returnToken) {
+ final List<?> list = discoveryCache.get(openidIdentifier);
+ if (list == null || list.isEmpty()) {
+ return null;
+ }
+
+ final String contextUrl = urlProvider.get();
+ final DiscoveryInformation discovered = manager.associate(list);
+ final UrlEncoded retTo = new UrlEncoded(contextUrl + RETURN_URL);
+ retTo.put(P_MODE, mode.name());
+ if (returnToken != null && returnToken.length() > 0) {
+ retTo.put(P_TOKEN, returnToken);
+ }
+ if (remember) {
+ retTo.put(P_REMEMBER, "1");
+ }
+ if (discovered.hasClaimedIdentifier()) {
+ retTo.put(P_CLAIMED, discovered.getClaimedIdentifier().getIdentifier());
+ }
+ return new State(discovered, retTo, contextUrl);
+ }
+
+ private static class State {
+ final DiscoveryInformation discovered;
+ final UrlEncoded retTo;
+ final String contextUrl;
+
+ State(final DiscoveryInformation d, final UrlEncoded r, final String c) {
+ discovered = d;
+ retTo = r;
+ contextUrl = c;
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
new file mode 100644
index 0000000000..3ecfa627c7
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -0,0 +1,326 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import eu.medsea.mimeutil.MimeType;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.NB;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Exports a single version of a patch as a normal file download.
+ * <p>
+ * This can be relatively unsafe with Microsoft Internet Explorer 6.0 as the
+ * browser will (rather incorrectly) treat an HTML or JavaScript file its
+ * supposed to download as though it was served by this site, and will execute
+ * it with the site's own protection domain. This opens a massive security hole
+ * so we package the content into a zip file.
+ */
+@SuppressWarnings("serial")
+@Singleton
+public class CatServlet extends HttpServlet {
+ private static final MimeType ZIP = new MimeType("application/zip");
+ private final Provider<ReviewDb> requestDb;
+ private final GitRepositoryManager repoManager;
+ private final SecureRandom rng;
+ private final FileTypeRegistry registry;
+ private final ChangeControl.Factory changeControl;
+
+ @Inject
+ CatServlet(final GitRepositoryManager grm, final Provider<ReviewDb> sf,
+ final FileTypeRegistry ftr, final ChangeControl.Factory ccf) {
+ requestDb = sf;
+ repoManager = grm;
+ rng = new SecureRandom();
+ registry = ftr;
+ changeControl = ccf;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ String keyStr = req.getPathInfo();
+
+ // We shouldn't have to do this extra decode pass, but somehow we
+ // are now receiving our "^1" suffix as "%5E1", which confuses us
+ // downstream. Other times we get our embedded "," as "%2C", which
+ // is equally bad. And yet when these happen a "%2F" is left as-is,
+ // rather than escaped as "%252F", which makes me feel really really
+ // uncomfortable with a blind decode right here.
+ //
+ keyStr = URLDecoder.decode(keyStr, "UTF-8");
+
+ if (!keyStr.startsWith("/")) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ keyStr = keyStr.substring(1);
+
+ final Patch.Key patchKey;
+ final int side;
+ {
+ final int c = keyStr.lastIndexOf('^');
+ if (c == 0) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (c < 0) {
+ side = 0;
+
+ } else {
+ try {
+ side = Integer.parseInt(keyStr.substring(c + 1));
+ keyStr = keyStr.substring(0, c);
+ } catch (NumberFormatException e) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ }
+
+ try {
+ patchKey = Patch.Key.parse(keyStr);
+ } catch (NumberFormatException e) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ }
+
+ final Change.Id changeId = patchKey.getParentKey().getParentKey();
+ final Project project;
+ final PatchSet patchSet;
+ try {
+ final ReviewDb db = requestDb.get();
+ final ChangeControl control = changeControl.validateFor(changeId);
+
+ project = control.getProject();
+ patchSet = db.patchSets().get(patchKey.getParentKey());
+ if (patchSet == null) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ } catch (NoSuchChangeException e) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ } catch (OrmException e) {
+ getServletContext().log("Cannot query database", e);
+ rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ final Repository repo;
+ try {
+ repo = repoManager.openRepository(project.getNameKey().get());
+ } catch (RepositoryNotFoundException e) {
+ getServletContext().log("Cannot open repository", e);
+ rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ final byte[] blobData;
+ final RevCommit fromCommit;
+ final String suffix;
+ final String path = patchKey.getFileName();
+ try {
+ final RevWalk rw = new RevWalk(repo);
+ final RevCommit c;
+ final TreeWalk tw;
+
+ c = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
+ if (side == 0) {
+ fromCommit = c;
+ suffix = "new";
+
+ } else if (1 <= side && side - 1 < c.getParentCount()) {
+ fromCommit = rw.parseCommit(c.getParent(side - 1));
+ if (c.getParentCount() == 1) {
+ suffix = "old";
+ } else {
+ suffix = "old" + side;
+ }
+
+ } else {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ tw = TreeWalk.forPath(repo, path, fromCommit.getTree());
+ if (tw == null) {
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ if (tw.getFileMode(0).getObjectType() == Constants.OBJ_BLOB) {
+ blobData = repo.openBlob(tw.getObjectId(0)).getCachedBytes();
+
+ } else {
+ rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ } catch (IOException e) {
+ getServletContext().log("Cannot read repository", e);
+ rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ } catch (RuntimeException e) {
+ getServletContext().log("Cannot read repository", e);
+ rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ } finally {
+ repo.close();
+ }
+
+ final long when = fromCommit.getCommitTime() * 1000L;
+ MimeType contentType = registry.getMimeType(path, blobData);
+ final byte[] outData;
+
+ if (registry.isSafeInline(contentType)) {
+ outData = blobData;
+
+ } else {
+ // The content may not be safe to transmit inline, as a browser might
+ // interpret it as HTML or JavaScript hosted by this site. Such code
+ // might then run in the site's security domain, and may be able to use
+ // the user's cookies to perform unauthorized actions.
+ //
+ // Usually, wrapping the content into a ZIP file forces the browser to
+ // save the content to the local system instead.
+ //
+ final ByteArrayOutputStream zip = new ByteArrayOutputStream();
+ final ZipOutputStream zo = new ZipOutputStream(zip);
+ final ZipEntry e = new ZipEntry(safeFileName(path, rand(req, suffix)));
+ e.setComment(fromCommit.name() + ":" + path);
+ e.setSize(blobData.length);
+ e.setTime(when);
+ zo.putNextEntry(e);
+ zo.write(blobData);
+ zo.closeEntry();
+ zo.close();
+
+ outData = zip.toByteArray();
+ contentType = ZIP;
+
+ rsp.setHeader("Content-Disposition", "attachment; filename=\""
+ + safeFileName(path, suffix) + ".zip" + "\"");
+ }
+
+ rsp.setContentType(contentType.toString());
+ rsp.setContentLength(outData.length);
+ rsp.setDateHeader("Last-Modified", when);
+ rsp.setDateHeader("Expires", 0L);
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.getOutputStream().write(outData);
+ }
+
+ private static String safeFileName(String fileName, final String suffix) {
+ // Convert a file path (e.g. "src/Init.c") to a safe file name with
+ // no meta-characters that might be unsafe on any given platform.
+ //
+ final int slash = fileName.lastIndexOf('/');
+ if (slash >= 0) {
+ fileName = fileName.substring(slash + 1);
+ }
+
+ final StringBuilder r = new StringBuilder(fileName.length());
+ for (int i = 0; i < fileName.length(); i++) {
+ final char c = fileName.charAt(i);
+ if (c == '_' || c == '-' || c == '.' || c == '@') {
+ r.append(c);
+
+ } else if ('0' <= c && c <= '9') {
+ r.append(c);
+
+ } else if ('A' <= c && c <= 'Z') {
+ r.append(c);
+
+ } else if ('a' <= c && c <= 'z') {
+ r.append(c);
+
+ } else if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ r.append('-');
+
+ } else {
+ r.append('_');
+ }
+ }
+ fileName = r.toString();
+
+ final int ext = fileName.lastIndexOf('.');
+ if (ext <= 0) {
+ return fileName + "_" + suffix;
+
+ } else {
+ return fileName.substring(0, ext) + "_" + suffix
+ + fileName.substring(ext);
+ }
+ }
+
+ private String rand(final HttpServletRequest req, final String suffix)
+ throws UnsupportedEncodingException {
+ // Produce a random suffix that is difficult (or nearly impossible)
+ // for an attacker to guess in advance. This reduces the risk that
+ // an attacker could upload a *.class file and have us send a ZIP
+ // that can be invoked through an applet tag in the victim's browser.
+ //
+ final MessageDigest md = Constants.newMessageDigest();
+ final byte[] buf = new byte[8];
+
+ NB.encodeInt32(buf, 0, req.getRemotePort());
+ md.update(req.getRemoteAddr().getBytes("UTF-8"));
+ md.update(buf, 0, 4);
+
+ NB.encodeInt64(buf, 0, System.currentTimeMillis());
+ md.update(buf, 0, 8);
+
+ rng.nextBytes(buf);
+ md.update(buf, 0, 8);
+
+ return suffix + "-" + ObjectId.fromRaw(md.digest()).name();
+ }
+
+}
diff --git a/src/main/webapp/WEB-INF/HostPage.html b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPage.html
index 423e7d04c5..423e7d04c5 100644
--- a/src/main/webapp/WEB-INF/HostPage.html
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPage.html
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
new file mode 100644
index 0000000000..cc5ff425f2
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/HostPageServlet.java
@@ -0,0 +1,249 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.common.data.GerritConfig;
+import com.google.gerrit.common.data.HostPageData;
+import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.config.SitePath;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.gwtjsonrpc.server.JsonServlet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.security.MessageDigest;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** Sends the Gerrit host page to clients. */
+@SuppressWarnings("serial")
+@Singleton
+public class HostPageServlet extends HttpServlet {
+ private final Provider<CurrentUser> currentUser;
+ private final File sitePath;
+ private final GerritConfig config;
+ private final Provider<String> urlProvider;
+ private final boolean wantSSL;
+ private final Document hostDoc;
+
+ @Inject
+ HostPageServlet(final Provider<CurrentUser> cu, @SitePath final File path,
+ final GerritConfig gc,
+ @CanonicalWebUrl @Nullable final Provider<String> up,
+ @CanonicalWebUrl @Nullable final String configuredUrl,
+ final ServletContext servletContext) throws IOException {
+ currentUser = cu;
+ urlProvider = up;
+ sitePath = path;
+ config = gc;
+ wantSSL = configuredUrl != null && configuredUrl.startsWith("https:");
+
+ final String pageName = "HostPage.html";
+ hostDoc = HtmlDomUtil.parseFile(getClass(), pageName);
+ if (hostDoc == null) {
+ throw new FileNotFoundException("No " + pageName + " in webapp");
+ }
+ fixModuleReference(hostDoc, servletContext);
+ injectCssFile(hostDoc, "gerrit_sitecss", sitePath, "GerritSite.css");
+ injectXmlFile(hostDoc, "gerrit_header", sitePath, "GerritSiteHeader.html");
+ injectXmlFile(hostDoc, "gerrit_footer", sitePath, "GerritSiteFooter.html");
+ }
+
+ private void injectXmlFile(final Document hostDoc, final String id,
+ final File sitePath, final String fileName) throws IOException {
+ final Element banner = HtmlDomUtil.find(hostDoc, id);
+ if (banner == null) {
+ return;
+ }
+
+ while (banner.getFirstChild() != null) {
+ banner.removeChild(banner.getFirstChild());
+ }
+
+ final Document html = HtmlDomUtil.parseFile(sitePath, fileName);
+ if (html == null) {
+ banner.getParentNode().removeChild(banner);
+ return;
+ }
+
+ final Element content = html.getDocumentElement();
+ banner.appendChild(hostDoc.importNode(content, true));
+ }
+
+ private void injectCssFile(final Document hostDoc, final String id,
+ final File sitePath, final String fileName) throws IOException {
+ final Element banner = HtmlDomUtil.find(hostDoc, id);
+ if (banner == null) {
+ return;
+ }
+
+ while (banner.getFirstChild() != null) {
+ banner.removeChild(banner.getFirstChild());
+ }
+
+ final String css = HtmlDomUtil.readFile(sitePath, fileName);
+ if (css == null) {
+ banner.getParentNode().removeChild(banner);
+ return;
+ }
+
+ banner.removeAttribute("id");
+ banner.appendChild(hostDoc.createCDATASection("\n" + css + "\n"));
+ }
+
+ private void injectJson(final Document hostDoc, final String id,
+ final Object obj) {
+ final Element scriptNode = HtmlDomUtil.find(hostDoc, id);
+ if (scriptNode == null) {
+ return;
+ }
+
+ while (scriptNode.getFirstChild() != null) {
+ scriptNode.removeChild(scriptNode.getFirstChild());
+ }
+
+ if (obj == null) {
+ scriptNode.getParentNode().removeChild(scriptNode);
+ return;
+ }
+
+ final StringWriter w = new StringWriter();
+ w.write("<!--\n");
+ w.write("var ");
+ w.write(id);
+ w.write("_obj=");
+ JsonServlet.defaultGsonBuilder().create().toJson(obj, w);
+ w.write(";\n// -->\n");
+ scriptNode.removeAttribute("id");
+ scriptNode.setAttribute("type", "text/javascript");
+ scriptNode.setAttribute("language", "javascript");
+ scriptNode.appendChild(hostDoc.createCDATASection(w.toString()));
+ }
+
+ private void fixModuleReference(final Document hostDoc,
+ final ServletContext servletContext) throws IOException {
+ final Element scriptNode = HtmlDomUtil.find(hostDoc, "gerrit_module");
+ if (scriptNode == null) {
+ throw new IOException("No gerrit_module to rewrite in host document");
+ }
+ scriptNode.removeAttribute("id");
+
+ final String src = scriptNode.getAttribute("src");
+ InputStream in = servletContext.getResourceAsStream("/" + src);
+ if (in == null) {
+ throw new IOException("No " + src + " in webapp root");
+ }
+
+ final MessageDigest md = Constants.newMessageDigest();
+ try {
+ try {
+ final byte[] buf = new byte[1024];
+ int n;
+ while ((n = in.read(buf)) > 0) {
+ md.update(buf, 0, n);
+ }
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new IOException("Failed reading " + src, e);
+ }
+
+ final String vstr = ObjectId.fromRaw(md.digest()).name();
+ scriptNode.setAttribute("src", src + "?content=" + vstr);
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ // If we wanted SSL, but the user didn't come to us over an SSL channel,
+ // force it to be SSL by issuing a protocol redirect. Try to keep the
+ // name "localhost" in case this is an SSH port tunnel.
+ //
+ if (wantSSL && !isSecure(req)) {
+ final StringBuffer reqUrl = req.getRequestURL();
+ if (isLocalHost(req)) {
+ reqUrl.replace(0, reqUrl.indexOf(":"), "https");
+ } else {
+ reqUrl.setLength(0);
+ reqUrl.append(urlProvider.get());
+ }
+ rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
+ rsp.setHeader("Location", reqUrl.toString());
+ return;
+ }
+
+ final HostPageData pageData = new HostPageData();
+ pageData.config = config;
+
+ final CurrentUser user = currentUser.get();
+ if (user instanceof IdentifiedUser) {
+ pageData.userAccount = ((IdentifiedUser) user).getAccount();
+ }
+
+ final Document peruser = HtmlDomUtil.clone(hostDoc);
+ injectJson(peruser, "gerrit_hostpagedata", pageData);
+
+ final byte[] raw = HtmlDomUtil.toUTF8(peruser);
+ final byte[] tosend;
+ if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ rsp.setHeader("Content-Encoding", "gzip");
+ tosend = HtmlDomUtil.compress(raw);
+ } else {
+ tosend = raw;
+ }
+
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.setContentType("text/html");
+ rsp.setCharacterEncoding(HtmlDomUtil.ENC);
+ rsp.setContentLength(tosend.length);
+ final OutputStream out = rsp.getOutputStream();
+ try {
+ out.write(tosend);
+ } finally {
+ out.close();
+ }
+ }
+
+ private static boolean isSecure(final HttpServletRequest req) {
+ return "https".equals(req.getScheme()) || req.isSecure();
+ }
+
+ private static boolean isLocalHost(final HttpServletRequest req) {
+ return "localhost".equals(req.getServerName())
+ || "127.0.0.1".equals(req.getServerName());
+ }
+}
diff --git a/src/main/webapp/WEB-INF/LegacyGerrit.html b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerrit.html
index 5050bf245f..5050bf245f 100644
--- a/src/main/webapp/WEB-INF/LegacyGerrit.html
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerrit.html
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
new file mode 100644
index 0000000000..5a72230931
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/LegacyGerritServlet.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Redirects from {@code /Gerrit#foo} to {@code /#foo} in JavaScript.
+ * <p>
+ * This redirect exists to convert the older /Gerrit URL into the more modern
+ * URL format which does not use a servlet name for the host page. We cannot do
+ * the redirect here in the server side, as it would lose any history token that
+ * appears in the URL. Instead we send an HTML page which instructs the browser
+ * to replace the URL, but preserve the history token.
+ */
+@SuppressWarnings("serial")
+@Singleton
+public class LegacyGerritServlet extends HttpServlet {
+ private final byte[] raw;
+ private final byte[] compressed;
+
+ @Inject
+ LegacyGerritServlet(final ServletContext servletContext) throws IOException {
+ final String pageName = "LegacyGerrit.html";
+ final String doc = HtmlDomUtil.readFile(getClass(), pageName);
+ if (doc == null) {
+ throw new FileNotFoundException("No " + pageName + " in webapp");
+ }
+
+ raw = doc.getBytes(HtmlDomUtil.ENC);
+ compressed = HtmlDomUtil.compress(raw);
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final byte[] tosend;
+ if (RPCServletUtils.acceptsGzipEncoding(req)) {
+ rsp.setHeader("Content-Encoding", "gzip");
+ tosend = compressed;
+ } else {
+ tosend = raw;
+ }
+
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.setContentType("text/html");
+ rsp.setCharacterEncoding(HtmlDomUtil.ENC);
+ rsp.setContentLength(tosend.length);
+ final OutputStream out = rsp.getOutputStream();
+ try {
+ out.write(tosend);
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/PrettifyServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/PrettifyServlet.java
new file mode 100644
index 0000000000..7b9bda2eb8
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/PrettifyServlet.java
@@ -0,0 +1,90 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Singleton
+public class PrettifyServlet extends HttpServlet {
+ private static final String VERSION = "20090521";
+
+ private final byte[] content;
+
+ @Inject
+ PrettifyServlet(final ServletContext servletContext) throws IOException {
+ final String myDir = "/gerrit/prettify" + VERSION + "/";
+ final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ load(buffer, servletContext, myDir + "prettify.js");
+ for (Object p : servletContext.getResourcePaths(myDir)) {
+ String name = (String) p;
+ if (name.startsWith(myDir + "lang-") && name.endsWith(".js")) {
+ load(buffer, servletContext, name);
+ }
+ }
+ content = buffer.toByteArray();
+ }
+
+ private void load(final OutputStream buffer,
+ final ServletContext servletContext, final String path)
+ throws IOException {
+ final InputStream in = servletContext.getResourceAsStream(path);
+ if (in != null) {
+ try {
+ final byte[] tmp = new byte[4096];
+ int cnt;
+ while ((cnt = in.read(tmp)) > 0) {
+ buffer.write(tmp, 0, cnt);
+ }
+ buffer.write(';');
+ buffer.write('\n');
+ in.close();
+ } catch (IOException e) {
+ throw new IOException("Cannot read " + path, e);
+ }
+ }
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final String want = req.getPathInfo();
+ if (want.equals("/" + VERSION + ".js")) {
+ final long now = System.currentTimeMillis();
+ rsp.setHeader("Cache-Control", "max-age=31536000,public");
+ rsp.setDateHeader("Expires", now + 31536000000L);
+ rsp.setDateHeader("Date", now);
+ rsp.setContentType("application/x-javascript");
+ rsp.setContentLength(content.length);
+ rsp.getOutputStream().write(content);
+ } else {
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+ rsp.setDateHeader("Date", System.currentTimeMillis());
+ rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
new file mode 100644
index 0000000000..6078c4efc2
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/SshInfoServlet.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import com.jcraft.jsch.HostKey;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet hosting an SSH daemon on another port. During a standard HTTP GET
+ * request the servlet returns the hostname and port number back to the client
+ * in the form <code>${host} ${port}</code>.
+ * <p>
+ * Use a Git URL such as <code>ssh://${email}@${host}:${port}/${path}</code>,
+ * e.g. <code>ssh://sop@google.com@gerrit.com:8010/tools/gerrit.git</code> to
+ * access the SSH daemon itself.
+ * <p>
+ * Versions of Git before 1.5.3 may require setting the username and port
+ * properties in the user's <code>~/.ssh/config</code> file, and using a host
+ * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git:
+ * <pre>
+ * Host gerrit-alias
+ * User sop@google.com
+ * Hostname gerrit.com
+ * Port 8010
+ * </pre>
+ */
+@SuppressWarnings("serial")
+@Singleton
+public class SshInfoServlet extends HttpServlet {
+ private final SshInfo sshd;
+
+ @Inject
+ SshInfoServlet(final SshInfo daemon) {
+ sshd = daemon;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
+ rsp.setHeader("Pragma", "no-cache");
+ rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
+
+ final List<HostKey> hostKeys = sshd.getHostKeys();
+ final String out;
+ if (!hostKeys.isEmpty()) {
+ String host = hostKeys.get(0).getHost();
+ String port = "22";
+
+ if (host.contains(":")) {
+ final int p = host.lastIndexOf(':');
+ port = host.substring(p + 1);
+ host = host.substring(0, p);
+ }
+
+ if (host.equals("*")) {
+ host = req.getServerName();
+
+ } else if (host.startsWith("[") && host.endsWith("]")) {
+ host = host.substring(1, host.length() - 1);
+ }
+
+ out = host + " " + port;
+ } else {
+ out = "NOT_AVAILABLE";
+ }
+
+ rsp.setCharacterEncoding("UTF-8");
+ rsp.setContentType("text/plain");
+ final PrintWriter w = rsp.getWriter();
+ try {
+ w.write(out);
+ } finally {
+ w.close();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java
new file mode 100644
index 0000000000..41ff2c8df9
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/StaticServlet.java
@@ -0,0 +1,157 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.raw;
+
+import com.google.gerrit.server.config.SitePath;
+import com.google.gwt.user.server.rpc.RPCServletUtils;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.util.NB;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/** Sends static content from the site 's <code>static/</code> subdirectory. */
+@SuppressWarnings("serial")
+@Singleton
+public class StaticServlet extends HttpServlet {
+ private static final long MAX_AGE = 12 * 60 * 60 * 1000L/* milliseconds */;
+ private static final String CACHE_CTRL =
+ "public, max-age=" + (MAX_AGE / 1000L);
+
+ private static final HashMap<String, String> MIME_TYPES =
+ new HashMap<String, String>();
+ static {
+ MIME_TYPES.put("html", "text/html");
+ MIME_TYPES.put("htm", "text/html");
+ MIME_TYPES.put("js", "application/x-javascript");
+ MIME_TYPES.put("css", "text/css");
+ MIME_TYPES.put("rtf", "text/rtf");
+ MIME_TYPES.put("txt", "text/plain");
+ MIME_TYPES.put("text", "text/plain");
+ MIME_TYPES.put("pdf", "application/pdf");
+ MIME_TYPES.put("jpeg", "image/jpeg");
+ MIME_TYPES.put("jpg", "image/jpeg");
+ MIME_TYPES.put("gif", "image/gif");
+ MIME_TYPES.put("png", "image/png");
+ MIME_TYPES.put("tiff", "image/tiff");
+ MIME_TYPES.put("tif", "image/tiff");
+ MIME_TYPES.put("svg", "image/svg+xml");
+ }
+
+ private static String contentType(final String name) {
+ final int dot = name.lastIndexOf('.');
+ final String ext = 0 < dot ? name.substring(dot + 1) : "";
+ final String type = MIME_TYPES.get(ext);
+ return type != null ? type : "application/octet-stream";
+ }
+
+ private static byte[] readFile(final File p) throws IOException {
+ final FileInputStream in = new FileInputStream(p);
+ try {
+ final byte[] r = new byte[(int) in.getChannel().size()];
+ NB.readFully(in, r, 0, r.length);
+ return r;
+ } finally {
+ in.close();
+ }
+ }
+
+ private static byte[] compress(final byte[] raw) throws IOException {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ final GZIPOutputStream gz = new GZIPOutputStream(out);
+ gz.write(raw);
+ gz.finish();
+ gz.flush();
+ return out.toByteArray();
+ }
+
+ private final File staticBase;
+
+ @Inject
+ StaticServlet(@SitePath final File sitePath) {
+ staticBase = new File(sitePath, "static");
+ }
+
+ private File local(final HttpServletRequest req) {
+ final String name = req.getPathInfo();
+ if (name.length() < 2 || !name.startsWith("/")) {
+ // Too short to be a valid file name, or doesn't start with
+ // the path info separator like we expected.
+ //
+ return null;
+ }
+
+ if (name.indexOf('/', 1) > 0 || name.indexOf('\\', 1) > 0) {
+ // Contains a path separator. Don't serve it as the client
+ // might be trying something evil like "/../../etc/passwd".
+ // This static servlet is just meant to facilitate simple
+ // assets like banner images.
+ //
+ return null;
+ }
+
+ final File p = new File(staticBase, name.substring(1));
+ return p.isFile() ? p : null;
+ }
+
+ @Override
+ protected long getLastModified(final HttpServletRequest req) {
+ final File p = local(req);
+ return p != null ? p.lastModified() : -1;
+ }
+
+ @Override
+ protected void doGet(final HttpServletRequest req,
+ final HttpServletResponse rsp) throws IOException {
+ final File p = local(req);
+ if (p == null) {
+ rsp.setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+
+ final String type = contentType(p.getName());
+ final byte[] tosend;
+ if (!type.equals("application/x-javascript")
+ && RPCServletUtils.acceptsGzipEncoding(req)) {
+ rsp.setHeader("Content-Encoding", "gzip");
+ tosend = compress(readFile(p));
+ } else {
+ tosend = readFile(p);
+ }
+
+ rsp.setHeader("Cache-Control", CACHE_CTRL);
+ rsp.setDateHeader("Expires", System.currentTimeMillis() + MAX_AGE);
+ rsp.setDateHeader("Last-Modified", p.lastModified());
+ rsp.setContentType(type);
+ rsp.setContentLength(tosend.length);
+ final OutputStream out = rsp.getOutputStream();
+ try {
+ out.write(tosend);
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
new file mode 100644
index 0000000000..b428c9a0ad
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/BaseServiceImplementation.java
@@ -0,0 +1,123 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.errors.CorruptEntityException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.NoSuchGroupException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Provider;
+
+/** Support for services which require a {@link ReviewDb} instance. */
+public class BaseServiceImplementation {
+ private final Provider<ReviewDb> schema;
+ private final Provider<? extends CurrentUser> currentUser;
+
+ protected BaseServiceImplementation(final Provider<ReviewDb> schema,
+ final Provider<? extends CurrentUser> currentUser) {
+ this.schema = schema;
+ this.currentUser = currentUser;
+ }
+
+ @Deprecated
+ protected Account.Id getAccountId() {
+ CurrentUser u = currentUser.get();
+ if (u instanceof IdentifiedUser) {
+ return ((IdentifiedUser) u).getAccountId();
+ }
+ return null;
+ }
+
+ /**
+ * Executes <code>action.run</code> with an active ReviewDb connection.
+ * <p>
+ * A database handle is automatically opened and closed around the action's
+ * {@link Action#run(ReviewDb)} method. OrmExceptions are caught and passed
+ * into the onFailure method of the callback.
+ *
+ * @param <T> type of result the callback expects.
+ * @param callback the callback that will receive the result.
+ * @param action the action logic to perform.
+ */
+ protected <T> void run(final AsyncCallback<T> callback, final Action<T> action) {
+ try {
+ final T r = action.run(schema.get());
+ if (r != null) {
+ callback.onSuccess(r);
+ }
+ } catch (NoSuchProjectException e) {
+ callback.onFailure(new NoSuchEntityException());
+ } catch (NoSuchGroupException e) {
+ callback.onFailure(new NoSuchEntityException());
+
+ } catch (OrmException e) {
+ if (e.getCause() instanceof Failure) {
+ callback.onFailure(e.getCause().getCause());
+
+ } else if (e.getCause() instanceof CorruptEntityException) {
+ callback.onFailure(e.getCause());
+
+ } else if (e.getCause() instanceof NoSuchEntityException) {
+ callback.onFailure(e.getCause());
+
+ } else {
+ callback.onFailure(e);
+ }
+ } catch (Failure e) {
+ if (e.getCause() instanceof NoSuchProjectException
+ || e.getCause() instanceof NoSuchChangeException
+ || e.getCause() instanceof NoSuchGroupException) {
+ callback.onFailure(new NoSuchEntityException());
+
+ } else {
+ callback.onFailure(e.getCause());
+ }
+ }
+ }
+
+ /** Exception whose cause is passed into onFailure. */
+ @Deprecated
+ public static class Failure extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public Failure(final Throwable why) {
+ super(why);
+ }
+ }
+
+ /** Arbitrary action to run with a database connection. */
+ public static interface Action<T> {
+ /**
+ * Perform this action, returning the onSuccess value.
+ *
+ * @param db an open database handle to be used by this connection.
+ * @return he value to pass to {@link AsyncCallback#onSuccess(Object)}.
+ * @throws OrmException any schema based action failed.
+ * @throws Failure cause is given to
+ * {@link AsyncCallback#onFailure(Throwable)}.
+ * @throws NoSuchProjectException
+ * @throws NoSuchGroupException
+ */
+ T run(ReviewDb db) throws OrmException, Failure, NoSuchProjectException,
+ NoSuchGroupException;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
new file mode 100644
index 0000000000..c85497086f
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
@@ -0,0 +1,598 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.data.AccountDashboardInfo;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.data.ChangeListService;
+import com.google.gerrit.common.data.SingleListChangeInfo;
+import com.google.gerrit.common.data.ToggleStarRequest;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeAccess;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.StarredChange;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.Transaction;
+import com.google.gwtorm.client.impl.ListResultSet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ChangeListServiceImpl extends BaseServiceImplementation implements
+ ChangeListService {
+ private static final Comparator<ChangeInfo> ID_COMP =
+ new Comparator<ChangeInfo>() {
+ public int compare(final ChangeInfo o1, final ChangeInfo o2) {
+ return o1.getId().get() - o2.getId().get();
+ }
+ };
+ private static final Comparator<ChangeInfo> SORT_KEY_COMP =
+ new Comparator<ChangeInfo>() {
+ public int compare(final ChangeInfo o1, final ChangeInfo o2) {
+ return o2.getSortKey().compareTo(o1.getSortKey());
+ }
+ };
+ private static final Comparator<Change> QUERY_PREV =
+ new Comparator<Change>() {
+ public int compare(final Change a, final Change b) {
+ return a.getSortKey().compareTo(b.getSortKey());
+ }
+ };
+ private static final Comparator<Change> QUERY_NEXT =
+ new Comparator<Change>() {
+ public int compare(final Change a, final Change b) {
+ return b.getSortKey().compareTo(a.getSortKey());
+ }
+ };
+
+ private static final int MAX_PER_PAGE = 100;
+
+ private static int safePageSize(final int pageSize) {
+ return 0 < pageSize && pageSize <= MAX_PER_PAGE ? pageSize : MAX_PER_PAGE;
+ }
+
+ private final Provider<CurrentUser> currentUser;
+ private final ChangeControl.Factory changeControlFactory;
+ private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
+
+ @Inject
+ ChangeListServiceImpl(final Provider<ReviewDb> schema,
+ final Provider<CurrentUser> currentUser,
+ final ChangeControl.Factory changeControlFactory,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory) {
+ super(schema, currentUser);
+ this.currentUser = currentUser;
+ this.changeControlFactory = changeControlFactory;
+ this.accountInfoCacheFactory = accountInfoCacheFactory;
+ }
+
+ private boolean canRead(final Change c) {
+ try {
+ return changeControlFactory.controlFor(c).isVisible();
+ } catch (NoSuchChangeException e) {
+ return false;
+ }
+ }
+
+ public void allOpenPrev(final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryPrev(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().allOpenPrev(sortKey, slim);
+ }
+ });
+ }
+
+ public void allOpenNext(final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryNext(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().allOpenNext(sortKey, slim);
+ }
+ });
+ }
+
+ public void byProjectOpenPrev(final Project.NameKey project,
+ final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryPrev(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().byProjectOpenPrev(project, sortKey, slim);
+ }
+ });
+ }
+
+ public void byProjectOpenNext(final Project.NameKey project,
+ final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryNext(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().byProjectOpenNext(project, sortKey, slim);
+ }
+ });
+ }
+
+ public void byProjectClosedPrev(final Project.NameKey project,
+ final Change.Status s, final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryPrev(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().byProjectClosedPrev(s.getCode(), project, sortKey,
+ slim);
+ }
+ });
+ }
+
+ public void byProjectClosedNext(final Project.NameKey project,
+ final Change.Status s, final String pos, final int pageSize,
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryNext(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
+ throws OrmException {
+ return db.changes().byProjectClosedNext(s.getCode(), project, sortKey,
+ slim);
+ }
+ });
+ }
+
+ public void allClosedPrev(final Change.Status s, final String pos,
+ final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryPrev(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int lim, String key)
+ throws OrmException {
+ return db.changes().allClosedPrev(s.getCode(), key, lim);
+ }
+ });
+ }
+
+ public void allClosedNext(final Change.Status s, final String pos,
+ final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryNext(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int lim, String key)
+ throws OrmException {
+ return db.changes().allClosedNext(s.getCode(), key, lim);
+ }
+ });
+ }
+
+ @Override
+ public void allQueryPrev(final String query, final String pos,
+ final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryPrev(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int lim, String key)
+ throws OrmException {
+ return searchQuery(db, query, lim, key, QUERY_PREV);
+ }
+ });
+ }
+
+ @Override
+ public void allQueryNext(final String query, final String pos,
+ final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new QueryNext(pageSize, pos) {
+ @Override
+ ResultSet<Change> query(ReviewDb db, int lim, String key)
+ throws OrmException {
+ return searchQuery(db, query, lim, key, QUERY_NEXT);
+ }
+ });
+ }
+
+ private ResultSet<Change> searchQuery(final ReviewDb db, String query,
+ final int limit, final String key, final Comparator<Change> cmp)
+ throws OrmException {
+ List<Change> result = new ArrayList<Change>();
+ final HashSet<Change.Id> want = new HashSet<Change.Id>();
+ query = query.trim();
+
+ if (query.matches("^[1-9][0-9]*$")) {
+ want.add(Change.Id.parse(query));
+
+ } else if (query.matches("^[iI][0-9a-f]{4,}.*$")) {
+ if (query.startsWith("i")) {
+ query = "I" + query.substring(1);
+ }
+ final Change.Key a = new Change.Key(query);
+ final Change.Key b = a.max();
+ filterBySortKey(result, db.changes().byKeyRange(a, b), cmp, key);
+ Collections.sort(result, cmp);
+ if (limit < result.size()) {
+ result = result.subList(0, limit);
+ }
+
+ } else if (query.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
+ final RevId id = new RevId(query);
+ final ResultSet<PatchSet> patches;
+ if (id.isComplete()) {
+ patches = db.patchSets().byRevision(id);
+ } else {
+ patches = db.patchSets().byRevisionRange(id, id.max());
+ }
+ for (PatchSet p : patches) {
+ want.add(p.getId().getParentKey());
+ }
+ } else if (query.contains("owner:")) {
+ String[] parsedQuery = query.split(":");
+ if (parsedQuery.length > 1) {
+ filterBySortKey(result, changesCreatedBy(db, parsedQuery[1]), cmp, key);
+ }
+ } else if (query.contains("reviewer:")) {
+ String[] parsedQuery = query.split(":");
+ if (parsedQuery.length > 1) {
+ want.addAll(changesReviewedBy(db, parsedQuery[1]));
+ }
+ }
+
+ if (result.isEmpty() && want.isEmpty()) {
+ return new ListResultSet<Change>(Collections.<Change> emptyList());
+ }
+
+ filterBySortKey(result, db.changes().get(want), cmp, key);
+ Collections.sort(result, cmp);
+ if (limit < result.size()) {
+ result = result.subList(0, limit);
+ }
+ return new ListResultSet<Change>(result);
+ }
+
+ private static void filterBySortKey(final List<Change> dst,
+ final Iterable<Change> src, final Comparator<Change> cmp, final String key) {
+ if (cmp == QUERY_PREV) {
+ for (Change c : src) {
+ if (c.getSortKey().compareTo(key) > 0) {
+ dst.add(c);
+ }
+ }
+ } else /* cmp == QUERY_NEXT */{
+ for (Change c : src) {
+ if (c.getSortKey().compareTo(key) < 0) {
+ dst.add(c);
+ }
+ }
+ }
+ }
+
+ public void forAccount(final Account.Id id,
+ final AsyncCallback<AccountDashboardInfo> callback) {
+ final Account.Id me = getAccountId();
+ final Account.Id target = id != null ? id : me;
+ if (target == null) {
+ callback.onFailure(new NoSuchEntityException());
+ return;
+ }
+
+ run(callback, new Action<AccountDashboardInfo>() {
+ public AccountDashboardInfo run(final ReviewDb db) throws OrmException,
+ Failure {
+ final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
+ final Account user = ac.get(target);
+ if (user == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final Set<Change.Id> stars = currentUser.get().getStarredChanges();
+ final ChangeAccess changes = db.changes();
+ final AccountDashboardInfo d;
+
+ final Set<Change.Id> openReviews = new HashSet<Change.Id>();
+ final Set<Change.Id> closedReviews = new HashSet<Change.Id>();
+ for (final PatchSetApproval ca : db.patchSetApprovals().openByUser(id)) {
+ openReviews.add(ca.getPatchSetId().getParentKey());
+ }
+ for (final PatchSetApproval ca : db.patchSetApprovals()
+ .closedByUser(id)) {
+ closedReviews.add(ca.getPatchSetId().getParentKey());
+ }
+
+ d = new AccountDashboardInfo(target);
+ d.setByOwner(filter(changes.byOwnerOpen(target), stars, ac));
+ d.setClosed(filter(changes.byOwnerClosed(target), stars, ac));
+
+ for (final ChangeInfo c : d.getByOwner()) {
+ openReviews.remove(c.getId());
+ }
+ d.setForReview(filter(changes.get(openReviews), stars, ac));
+ Collections.sort(d.getForReview(), ID_COMP);
+
+ for (final ChangeInfo c : d.getClosed()) {
+ closedReviews.remove(c.getId());
+ }
+ if (!closedReviews.isEmpty()) {
+ d.getClosed().addAll(filter(changes.get(closedReviews), stars, ac));
+ Collections.sort(d.getClosed(), SORT_KEY_COMP);
+ }
+
+ d.setAccounts(ac.create());
+ return d;
+ }
+ });
+ }
+
+ public void myStarredChanges(
+ final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new Action<SingleListChangeInfo>() {
+ public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
+ final Account.Id me = getAccountId();
+ final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
+ final SingleListChangeInfo d = new SingleListChangeInfo();
+ final Set<Change.Id> starred = currentUser.get().getStarredChanges();
+ d.setChanges(filter(db.changes().get(starred), starred, ac));
+ Collections.sort(d.getChanges(), new Comparator<ChangeInfo>() {
+ public int compare(final ChangeInfo o1, final ChangeInfo o2) {
+ return o1.getLastUpdatedOn().compareTo(o2.getLastUpdatedOn());
+ }
+ });
+ d.setAccounts(ac.create());
+ return d;
+ }
+ });
+ }
+
+ public void myDraftChanges(final AsyncCallback<SingleListChangeInfo> callback) {
+ run(callback, new Action<SingleListChangeInfo>() {
+ public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
+ final Account.Id me = getAccountId();
+ final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
+ final SingleListChangeInfo d = new SingleListChangeInfo();
+ final Set<Change.Id> starred = currentUser.get().getStarredChanges();
+ final Set<Change.Id> drafted = draftedBy(db, me);
+ d.setChanges(filter(db.changes().get(drafted), starred, ac));
+ Collections.sort(d.getChanges(), new Comparator<ChangeInfo>() {
+ public int compare(final ChangeInfo o1, final ChangeInfo o2) {
+ return o1.getLastUpdatedOn().compareTo(o2.getLastUpdatedOn());
+ }
+ });
+ d.setAccounts(ac.create());
+ return d;
+ }
+ });
+ }
+
+ public void toggleStars(final ToggleStarRequest req,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException {
+ final Account.Id me = getAccountId();
+ final Set<Change.Id> existing = currentUser.get().getStarredChanges();
+ final ArrayList<StarredChange> add = new ArrayList<StarredChange>();
+ final ArrayList<StarredChange> remove = new ArrayList<StarredChange>();
+
+ if (req.getAddSet() != null) {
+ for (final Change.Id id : req.getAddSet()) {
+ if (!existing.contains(id)) {
+ add.add(new StarredChange(new StarredChange.Key(me, id)));
+ }
+ }
+ }
+
+ if (req.getRemoveSet() != null) {
+ for (final Change.Id id : req.getRemoveSet()) {
+ if (existing.contains(id)) {
+ remove.add(new StarredChange(new StarredChange.Key(me, id)));
+ }
+ }
+ }
+
+ if (!add.isEmpty() || !remove.isEmpty()) {
+ final Transaction txn = db.beginTransaction();
+ db.starredChanges().insert(add);
+ db.starredChanges().delete(remove);
+ txn.commit();
+ }
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void myStarredChangeIds(final AsyncCallback<Set<Change.Id>> callback) {
+ callback.onSuccess(currentUser.get().getStarredChanges());
+ }
+
+ private List<ChangeInfo> filter(final ResultSet<Change> rs,
+ final Set<Change.Id> starred, final AccountInfoCacheFactory accts) {
+ final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>();
+ for (final Change c : rs) {
+ if (canRead(c)) {
+ final ChangeInfo ci = new ChangeInfo(c);
+ accts.want(ci.getOwner());
+ ci.setStarred(starred.contains(ci.getId()));
+ r.add(ci);
+ }
+ }
+ return r;
+ }
+
+ private static Set<Change.Id> draftedBy(final ReviewDb db, final Account.Id me)
+ throws OrmException {
+ final Set<Change.Id> existing = new HashSet<Change.Id>();
+ if (me != null) {
+ for (final PatchLineComment sc : db.patchComments().draftByAuthor(me)) {
+ final Change.Id c =
+ sc.getKey().getParentKey().getParentKey().getParentKey();
+ existing.add(c);
+ }
+ }
+ return existing;
+ }
+
+ /**
+ * @return a set of all the account ID's matching the given user name in
+ * either of the following columns: ssh name, email address, full name
+ */
+ private static Set<Account.Id> getAccountSources(final ReviewDb db,
+ final String userName) throws OrmException {
+ Set<Account.Id> result = new HashSet<Account.Id>();
+ String a = userName;
+ String b = userName + "\u9fa5";
+ addAll(result, db.accounts().suggestBySshUserName(a, b, 10));
+ addAll(result, db.accounts().suggestByFullName(a, b, 10));
+ for (AccountExternalId extId : db.accountExternalIds()
+ .suggestByEmailAddress(a, b, 10)) {
+ result.add(extId.getAccountId());
+ }
+ return result;
+ }
+
+ private static void addAll(Set<Account.Id> result, ResultSet<Account> rs) {
+ for (Account account : rs) {
+ result.add(account.getId());
+ }
+ }
+
+ /**
+ * @return a set of all the changes created by userName. This method tries to
+ * find userName in 1) the ssh user names, 2) the full names and 3)
+ * the email addresses. The returned changes are unique and sorted by
+ * time stamp, newer first.
+ */
+ private List<Change> changesCreatedBy(final ReviewDb db, final String userName)
+ throws OrmException {
+ final List<Change> resultChanges = new ArrayList<Change>();
+ for (Account.Id account : getAccountSources(db, userName)) {
+ for (Change change : db.changes().byOwnerOpen(account)) {
+ resultChanges.add(change);
+ }
+ for (Change change : db.changes().byOwnerClosedAll(account)) {
+ resultChanges.add(change);
+ }
+ }
+ return resultChanges;
+ }
+
+ /**
+ * @return a set of all the changes reviewed by userName. This method tries to
+ * find userName in 1) the ssh user names, 2) the full names and the
+ * email addresses. The returned changes are unique and sorted by time
+ * stamp, newer first.
+ */
+ private Set<Change.Id> changesReviewedBy(final ReviewDb db,
+ final String userName) throws OrmException {
+ final Set<Change.Id> resultChanges = new HashSet<Change.Id>();
+ for (Account.Id account : getAccountSources(db, userName)) {
+ for (PatchSetApproval a : db.patchSetApprovals().openByUser(account)) {
+ resultChanges.add(a.getPatchSetId().getParentKey());
+ }
+ for (PatchSetApproval a : db.patchSetApprovals().closedByUserAll(account)) {
+ resultChanges.add(a.getPatchSetId().getParentKey());
+ }
+ }
+ return resultChanges;
+ }
+
+ private abstract class QueryNext implements Action<SingleListChangeInfo> {
+ protected final String pos;
+ protected final int limit;
+ protected final int slim;
+
+ QueryNext(final int pageSize, final String pos) {
+ this.pos = pos;
+ this.limit = safePageSize(pageSize);
+ this.slim = limit + 1;
+ }
+
+ public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
+ final Account.Id me = getAccountId();
+ final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
+ final SingleListChangeInfo d = new SingleListChangeInfo();
+ final Set<Change.Id> starred = currentUser.get().getStarredChanges();
+
+ boolean results = true;
+ String sortKey = pos;
+ final ArrayList<ChangeInfo> list = new ArrayList<ChangeInfo>();
+ while (results && list.size() < slim) {
+ results = false;
+ final ResultSet<Change> rs = query(db, slim, sortKey);
+ for (final Change c : rs) {
+ results = true;
+ if (canRead(c)) {
+ final ChangeInfo ci = new ChangeInfo(c);
+ ac.want(ci.getOwner());
+ ci.setStarred(starred.contains(ci.getId()));
+ list.add(ci);
+ if (list.size() == slim) {
+ rs.close();
+ break;
+ }
+ }
+ sortKey = c.getSortKey();
+ }
+ }
+
+ final boolean atEnd = finish(list);
+ d.setChanges(list, atEnd);
+ d.setAccounts(ac.create());
+ return d;
+ }
+
+ boolean finish(final ArrayList<ChangeInfo> list) {
+ final boolean atEnd = list.size() <= limit;
+ if (list.size() == slim) {
+ list.remove(limit);
+ }
+ return atEnd;
+ }
+
+ abstract ResultSet<Change> query(final ReviewDb db, final int slim,
+ String sortKey) throws OrmException;
+ }
+
+ private abstract class QueryPrev extends QueryNext {
+ QueryPrev(int pageSize, String pos) {
+ super(pageSize, pos);
+ }
+
+ @Override
+ boolean finish(final ArrayList<ChangeInfo> list) {
+ final boolean atEnd = super.finish(list);
+ Collections.reverse(list);
+ return atEnd;
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
new file mode 100644
index 0000000000..8f7754a55c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServlet.java
@@ -0,0 +1,124 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.auth.SignInRequired;
+import com.google.gerrit.common.errors.NotSignedInException;
+import com.google.gerrit.httpd.WebSession;
+import com.google.gson.GsonBuilder;
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.gwtjsonrpc.server.ActiveCall;
+import com.google.gwtjsonrpc.server.JsonServlet;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Base JSON servlet to ensure the current user is not forged.
+ */
+@SuppressWarnings("serial")
+final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall> {
+ private final Provider<WebSession> session;
+ private final RemoteJsonService service;
+
+ @Inject
+ GerritJsonServlet(final Provider<WebSession> w, final RemoteJsonService s) {
+ session = w;
+ service = s;
+ }
+
+ @Override
+ protected GerritCall createActiveCall(final HttpServletRequest req,
+ final HttpServletResponse rsp) {
+ return new GerritCall(session.get(), req, rsp);
+ }
+
+ @Override
+ protected GsonBuilder createGsonBuilder() {
+ final GsonBuilder g = super.createGsonBuilder();
+
+ g.registerTypeAdapter(org.eclipse.jgit.diff.Edit.class,
+ new org.eclipse.jgit.diff.EditDeserializer());
+
+ return g;
+ }
+
+ @Override
+ protected void preInvoke(final GerritCall call) {
+ super.preInvoke(call);
+
+ if (call.isComplete()) {
+ return;
+ }
+
+ if (call.getMethod().getAnnotation(SignInRequired.class) != null) {
+ // If SignInRequired is set on this method we must have both a
+ // valid XSRF token *and* have the user signed in. Doing these
+ // checks also validates that they agree on the user identity.
+ //
+ if (!call.requireXsrfValid() || !session.get().isSignedIn()) {
+ call.onFailure(new NotSignedInException());
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected Object createServiceHandle() {
+ return service;
+ }
+
+ static class GerritCall extends ActiveCall {
+ private final WebSession session;
+
+ GerritCall(final WebSession session, final HttpServletRequest i,
+ final HttpServletResponse o) {
+ super(i, o);
+ this.session = session;
+ }
+
+ @Override
+ public void onFailure(final Throwable error) {
+ if (error instanceof IllegalArgumentException
+ || error instanceof IllegalStateException) {
+ super.onFailure(error);
+ } else if (error instanceof OrmException
+ || error instanceof RuntimeException) {
+ onInternalFailure(error);
+ } else {
+ super.onFailure(error);
+ }
+ }
+
+ @Override
+ public boolean xsrfValidate() {
+ final String keyIn = getXsrfKeyIn();
+ if (keyIn == null || "".equals(keyIn)) {
+ // Anonymous requests don't need XSRF protection, they shouldn't
+ // be able to cause critical state changes.
+ //
+ return !session.isSignedIn();
+
+ } else {
+ // The session must exist, and must be using this token.
+ //
+ return session.isSignedIn() && session.isTokenValid(keyIn);
+ }
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java
new file mode 100644
index 0000000000..823ee7a60a
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/GerritJsonServletProvider.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+
+/** Creates {@link GerritJsonServlet} with a {@link RemoteJsonService}. */
+class GerritJsonServletProvider implements Provider<GerritJsonServlet> {
+ @Inject
+ private Injector injector;
+
+ private final Class<? extends RemoteJsonService> serviceClass;
+
+ @Inject
+ GerritJsonServletProvider(final Class<? extends RemoteJsonService> c) {
+ serviceClass = c;
+ }
+
+ @Override
+ public GerritJsonServlet get() {
+ final RemoteJsonService srv = injector.getInstance(serviceClass);
+ return injector.createChildInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(RemoteJsonService.class).toInstance(srv);
+ }
+ }).getInstance(GerritJsonServlet.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/Handler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/Handler.java
new file mode 100644
index 0000000000..0c0a8ce655
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/Handler.java
@@ -0,0 +1,94 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.errors.CorruptEntityException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Base class for RPC service implementations.
+ * <p>
+ * Typically an RPC service implementation will extend this class and use Guice
+ * injection to manage its state. For example:
+ *
+ * <pre>
+ * class Foo extends Handler&lt;Result&gt; {
+ * interface Factory {
+ * Foo create(... args ...);
+ * }
+ * &#064;Inject
+ * Foo(state, @Assisted args) { ... }
+ * Result get() throws Exception { ... }
+ * }
+ * </pre>
+ *
+ * @param <T> type of result for {@link AsyncCallback#onSuccess(Object)} if the
+ * operation completed successfully.
+ */
+public abstract class Handler<T> implements Callable<T> {
+ /**
+ * Run the operation and pass the result to the callback.
+ *
+ * @param callback callback to receive the result of {@link #call()}.
+ */
+ public final void to(final AsyncCallback<T> callback) {
+ try {
+ final T r = call();
+ if (r != null) {
+ callback.onSuccess(r);
+ }
+ } catch (NoSuchProjectException e) {
+ callback.onFailure(new NoSuchEntityException());
+
+ } catch (NoSuchChangeException e) {
+ callback.onFailure(new NoSuchEntityException());
+
+ } catch (OrmException e) {
+ if (e.getCause() instanceof BaseServiceImplementation.Failure) {
+ callback.onFailure(e.getCause().getCause());
+
+ } else if (e.getCause() instanceof CorruptEntityException) {
+ callback.onFailure(e.getCause());
+
+ } else if (e.getCause() instanceof NoSuchEntityException) {
+ callback.onFailure(e.getCause());
+
+ } else {
+ callback.onFailure(e);
+ }
+ } catch (BaseServiceImplementation.Failure e) {
+ callback.onFailure(e.getCause());
+ } catch (Exception e) {
+ callback.onFailure(e);
+ }
+ }
+
+ /**
+ * Compute the operation result.
+ *
+ * @return the result of the operation. Return {@link VoidResult#INSTANCE} if
+ * there is no meaningful return value for the operation.
+ * @throws Exception the operation failed. The caller will log the exception
+ * and the stack trace, if it is worth logging on the server side.
+ */
+ public abstract T call() throws Exception;
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/RpcServletModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/RpcServletModule.java
new file mode 100644
index 0000000000..29abee6382
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/RpcServletModule.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gwtjsonrpc.client.RemoteJsonService;
+import com.google.inject.Key;
+import com.google.inject.Scopes;
+import com.google.inject.internal.UniqueAnnotations;
+import com.google.inject.servlet.ServletModule;
+
+/** Binds {@link RemoteJsonService} implementations to a JSON servlet. */
+public abstract class RpcServletModule extends ServletModule {
+ public static final String PREFIX = "/gerrit/rpc/";
+
+ private final String prefix;
+
+ protected RpcServletModule(final String pathPrefix) {
+ prefix = pathPrefix;
+ }
+
+ protected void rpc(Class<? extends RemoteJsonService> clazz) {
+ String name = clazz.getSimpleName();
+ if (name.endsWith("Impl")) {
+ name = name.substring(0, name.length() - 4);
+ }
+ rpc(name, clazz);
+ }
+
+ protected void rpc(final String name, Class<? extends RemoteJsonService> clazz) {
+ final Key<GerritJsonServlet> srv =
+ Key.get(GerritJsonServlet.class, UniqueAnnotations.create());
+ final GerritJsonServletProvider provider =
+ new GerritJsonServletProvider(clazz);
+ bind(clazz);
+ serve(prefix + name).with(srv);
+ bind(srv).toProvider(provider).in(Scopes.SINGLETON);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
new file mode 100644
index 0000000000..de800bba60
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
@@ -0,0 +1,125 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.SuggestService;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+class SuggestServiceImpl extends BaseServiceImplementation implements
+ SuggestService {
+ private static final String MAX_SUFFIX = "\u9fa5";
+
+ private final ProjectCache projectCache;
+ private final AccountCache accountCache;
+ private final Provider<CurrentUser> currentUser;
+
+ @Inject
+ SuggestServiceImpl(final Provider<ReviewDb> schema,
+ final ProjectCache projectCache, final AccountCache accountCache,
+ final Provider<CurrentUser> currentUser) {
+ super(schema, currentUser);
+ this.projectCache = projectCache;
+ this.accountCache = accountCache;
+ this.currentUser = currentUser;
+ }
+
+ public void suggestProjectNameKey(final String query, final int limit,
+ final AsyncCallback<List<Project.NameKey>> callback) {
+ run(callback, new Action<List<Project.NameKey>>() {
+ public List<Project.NameKey> run(final ReviewDb db) throws OrmException {
+ final String a = query;
+ final String b = a + MAX_SUFFIX;
+ final int max = 10;
+ final int n = limit <= 0 ? max : Math.min(limit, max);
+
+ final CurrentUser user = currentUser.get();
+ final List<Project.NameKey> r = new ArrayList<Project.NameKey>();
+ for (final Project p : db.projects().suggestByName(a, b, n)) {
+ final ProjectState e = projectCache.get(p.getNameKey());
+ if (e != null && e.controlFor(user).isVisible()) {
+ r.add(p.getNameKey());
+ }
+ }
+ return r;
+ }
+ });
+ }
+
+ public void suggestAccount(final String query, final int limit,
+ final AsyncCallback<List<AccountInfo>> callback) {
+ run(callback, new Action<List<AccountInfo>>() {
+ public List<AccountInfo> run(final ReviewDb db) throws OrmException {
+ final String a = query;
+ final String b = a + MAX_SUFFIX;
+ final int max = 10;
+ final int n = limit <= 0 ? max : Math.min(limit, max);
+
+ final LinkedHashMap<Account.Id, AccountInfo> r =
+ new LinkedHashMap<Account.Id, AccountInfo>();
+ for (final Account p : db.accounts().suggestByFullName(a, b, n)) {
+ r.put(p.getId(), new AccountInfo(p));
+ }
+ if (r.size() < n) {
+ for (final Account p : db.accounts().suggestByPreferredEmail(a, b,
+ n - r.size())) {
+ r.put(p.getId(), new AccountInfo(p));
+ }
+ }
+ if (r.size() < n) {
+ for (final AccountExternalId e : db.accountExternalIds()
+ .suggestByEmailAddress(a, b, n - r.size())) {
+ if (!r.containsKey(e.getAccountId())) {
+ final Account p = accountCache.get(e.getAccountId()).getAccount();
+ final AccountInfo info = new AccountInfo(p);
+ info.setPreferredEmail(e.getEmailAddress());
+ r.put(e.getAccountId(), info);
+ }
+ }
+ }
+ return new ArrayList<AccountInfo>(r.values());
+ }
+ });
+ }
+
+ public void suggestAccountGroup(final String query, final int limit,
+ final AsyncCallback<List<AccountGroup>> callback) {
+ run(callback, new Action<List<AccountGroup>>() {
+ public List<AccountGroup> run(final ReviewDb db) throws OrmException {
+ final String a = query;
+ final String b = a + MAX_SUFFIX;
+ final int max = 10;
+ final int n = limit <= 0 ? max : Math.min(limit, max);
+ return db.accountGroups().suggestByName(a, b, n).toList();
+ }
+ });
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java
new file mode 100644
index 0000000000..fb2bdf1dd6
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SystemInfoServiceImpl.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.common.data.SshHostKey;
+import com.google.gerrit.common.data.SystemInfoService;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import com.jcraft.jsch.HostKey;
+import com.jcraft.jsch.JSch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+class SystemInfoServiceImpl implements SystemInfoService {
+ private static final JSch JSCH = new JSch();
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final List<HostKey> hostKeys;
+ private final Provider<HttpServletRequest> httpRequest;
+
+ @Inject
+ SystemInfoServiceImpl(final SchemaFactory<ReviewDb> sf, final SshInfo daemon,
+ final Provider<HttpServletRequest> hsr) {
+ schema = sf;
+ hostKeys = daemon.getHostKeys();
+ httpRequest = hsr;
+ }
+
+ public void contributorAgreements(
+ final AsyncCallback<List<ContributorAgreement>> callback) {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ callback.onSuccess(db.contributorAgreements().active().toList());
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ callback.onFailure(e);
+ }
+ }
+
+ public void daemonHostKeys(final AsyncCallback<List<SshHostKey>> callback) {
+ final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(hostKeys.size());
+ for (final HostKey hk : hostKeys) {
+ String host = hk.getHost();
+ if (host.startsWith("*:")) {
+ final String port = host.substring(2);
+ host = "[" + httpRequest.get().getServerName() + "]:" + port;
+ }
+ final String fp = hk.getFingerPrint(JSCH);
+ r.add(new SshHostKey(host, hk.getType() + " " + hk.getKey(), fp));
+ }
+ callback.onSuccess(r);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
new file mode 100644
index 0000000000..7de332aad5
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc;
+
+import com.google.gerrit.httpd.rpc.account.AccountModule;
+import com.google.gerrit.httpd.rpc.changedetail.ChangeModule;
+import com.google.gerrit.httpd.rpc.patch.PatchModule;
+import com.google.gerrit.httpd.rpc.project.ProjectModule;
+
+/** Registers servlets to answer RPCs from client UI. */
+public class UiRpcModule extends RpcServletModule {
+ public UiRpcModule() {
+ super(PREFIX);
+ }
+
+ @Override
+ protected void configureServlets() {
+ rpc(ChangeListServiceImpl.class);
+ rpc(SuggestServiceImpl.class);
+ rpc(SystemInfoServiceImpl.class);
+
+ install(new AccountModule());
+ install(new ChangeModule());
+ install(new PatchModule());
+ install(new ProjectModule());
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
new file mode 100644
index 0000000000..ab7f857b8c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountModule.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.gerrit.server.config.FactoryModule;
+
+public class AccountModule extends RpcServletModule {
+ public AccountModule() {
+ super(UiRpcModule.PREFIX);
+ }
+
+ @Override
+ protected void configureServlets() {
+ install(new FactoryModule() {
+ @Override
+ protected void configure() {
+ factory(AgreementInfoFactory.Factory.class);
+ factory(ExternalIdDetailFactory.Factory.class);
+ factory(GroupDetailFactory.Factory.class);
+ factory(MyGroupsFactory.Factory.class);
+ }
+ });
+ rpc(AccountSecurityImpl.class);
+ rpc(AccountServiceImpl.class);
+ rpc(GroupAdminServiceImpl.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
new file mode 100644
index 0000000000..6f85e4287b
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountSecurityImpl.java
@@ -0,0 +1,385 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.common.data.AccountSecurity;
+import com.google.gerrit.common.errors.ContactInformationStoreException;
+import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.common.errors.InvalidSshUserNameException;
+import com.google.gerrit.common.errors.NameAlreadyUsedException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gerrit.reviewdb.ContactInformation;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountByEmailCache;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.contact.ContactStore;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.RegisterNewEmailSender;
+import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtjsonrpc.server.ValidToken;
+import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.util.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+class AccountSecurityImpl extends BaseServiceImplementation implements
+ AccountSecurity {
+
+ private static final Pattern SSH_USER_NAME_PATTERN = Pattern.compile(Account.SSH_USER_NAME_PATTERN);
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final ContactStore contactStore;
+ private final AuthConfig authConfig;
+ private final Realm realm;
+ private final RegisterNewEmailSender.Factory registerNewEmailFactory;
+ private final SshKeyCache sshKeyCache;
+ private final AccountByEmailCache byEmailCache;
+ private final AccountCache accountCache;
+ private final AccountManager accountManager;
+ private final boolean useContactInfo;
+
+ private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
+ private final MyGroupsFactory.Factory myGroupsFactory;
+
+ @Inject
+ AccountSecurityImpl(final Provider<ReviewDb> schema,
+ final Provider<CurrentUser> currentUser, final ContactStore cs,
+ final AuthConfig ac, final Realm r,
+ final RegisterNewEmailSender.Factory esf, final SshKeyCache skc,
+ final AccountByEmailCache abec, final AccountCache uac,
+ final AccountManager am,
+ final ExternalIdDetailFactory.Factory externalIdDetailFactory,
+ final MyGroupsFactory.Factory myGroupsFactory) {
+ super(schema, currentUser);
+ contactStore = cs;
+ authConfig = ac;
+ realm = r;
+ registerNewEmailFactory = esf;
+ sshKeyCache = skc;
+ byEmailCache = abec;
+ accountCache = uac;
+ accountManager = am;
+
+ useContactInfo = contactStore != null && contactStore.isEnabled();
+
+ this.externalIdDetailFactory = externalIdDetailFactory;
+ this.myGroupsFactory = myGroupsFactory;
+ }
+
+ public void mySshKeys(final AsyncCallback<List<AccountSshKey>> callback) {
+ run(callback, new Action<List<AccountSshKey>>() {
+ public List<AccountSshKey> run(ReviewDb db) throws OrmException {
+ return db.accountSshKeys().byAccount(getAccountId()).toList();
+ }
+ });
+ }
+
+ public void addSshKey(final String keyText,
+ final AsyncCallback<AccountSshKey> callback) {
+ run(callback, new Action<AccountSshKey>() {
+ public AccountSshKey run(final ReviewDb db) throws OrmException, Failure {
+ int max = 0;
+ final Account.Id me = getAccountId();
+ for (final AccountSshKey k : db.accountSshKeys().byAccount(me)) {
+ max = Math.max(max, k.getKey().get());
+ }
+
+ final AccountSshKey key;
+ try {
+ key = sshKeyCache.create(new AccountSshKey.Id(me, max + 1), keyText);
+ } catch (InvalidSshKeyException e) {
+ throw new Failure(e);
+ }
+ db.accountSshKeys().insert(Collections.singleton(key));
+ uncacheSshKeys(me);
+ return key;
+ }
+ });
+ }
+
+ public void deleteSshKeys(final Set<AccountSshKey.Id> ids,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final Account.Id me = getAccountId();
+ for (final AccountSshKey.Id keyId : ids) {
+ if (!me.equals(keyId.getParentKey()))
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final List<AccountSshKey> k = db.accountSshKeys().get(ids).toList();
+ if (!k.isEmpty()) {
+ final Transaction txn = db.beginTransaction();
+ db.accountSshKeys().delete(k, txn);
+ txn.commit();
+ uncacheSshKeys(me);
+ }
+
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ private void uncacheSshKeys(final Account.Id me) {
+ uncacheSshKeys(accountCache.get(me).getAccount().getSshUserName());
+ }
+
+ private void uncacheSshKeys(final String userName) {
+ sshKeyCache.evict(userName);
+ }
+
+ @Override
+ public void changeSshUserName(final String newName,
+ final AsyncCallback<VoidResult> callback) {
+ if (!realm.allowsEdit(Account.FieldName.SSH_USER_NAME)) {
+ callback.onFailure(new NameAlreadyUsedException());
+ return;
+ }
+
+ run(callback, new Action<VoidResult>() {
+ @Override
+ public VoidResult run(ReviewDb db) throws OrmException, Failure {
+ final Account me = db.accounts().get(getAccountId());
+ if (me == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ if (newName != null && !SSH_USER_NAME_PATTERN.matcher(newName).matches()) {
+ throw new Failure(new InvalidSshUserNameException());
+ }
+ final Account other;
+ if (newName != null) {
+ other = db.accounts().bySshUserName(newName);
+ } else {
+ other = null;
+ }
+
+ if (other != null) {
+ if (other.getId().equals(me.getId())) {
+ return VoidResult.INSTANCE;
+ } else {
+ throw new Failure(new NameAlreadyUsedException());
+ }
+ }
+
+ final String oldName = me.getSshUserName();
+ me.setSshUserName(newName);
+ db.accounts().update(Collections.singleton(me));
+ uncacheSshKeys(oldName);
+ uncacheSshKeys(newName);
+ accountCache.evict(me.getId());
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void myExternalIds(AsyncCallback<List<AccountExternalId>> callback) {
+ externalIdDetailFactory.create().to(callback);
+ }
+
+ @Override
+ public void myGroups(final AsyncCallback<List<AccountGroup>> callback) {
+ myGroupsFactory.create().to(callback);
+ }
+
+ public void deleteExternalIds(final Set<AccountExternalId.Key> keys,
+ final AsyncCallback<Set<AccountExternalId.Key>> callback) {
+ run(callback, new Action<Set<AccountExternalId.Key>>() {
+ public Set<AccountExternalId.Key> run(final ReviewDb db)
+ throws OrmException, Failure {
+ // Determine the records we will allow the user to remove.
+ //
+ final Account.Id me = getAccountId();
+ final Map<AccountExternalId.Key, AccountExternalId> all =
+ db.accountExternalIds()
+ .toMap(db.accountExternalIds().byAccount(me));
+
+ // Don't permit deletes unless they are for our own account
+ //
+ for (final AccountExternalId.Key keyId : keys) {
+ if (!all.containsKey(keyId))
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final AccountExternalId mostRecent =
+ AccountExternalId.mostRecent(all.values());
+ final Set<AccountExternalId.Key> removed =
+ new HashSet<AccountExternalId.Key>();
+ final List<AccountExternalId> toDelete =
+ new ArrayList<AccountExternalId>();
+ for (final AccountExternalId.Key k : keys) {
+ final AccountExternalId e = all.get(k);
+ if (e == null) {
+ // Its already gone, tell the client its gone
+ //
+ removed.add(k);
+
+ } else if (e == mostRecent) {
+ // Don't delete the most recently accessed identity; the
+ // user might lock themselves out of the account.
+ //
+ continue;
+
+ } else {
+ toDelete.add(e);
+ removed.add(e.getKey());
+ }
+ }
+
+ if (!toDelete.isEmpty()) {
+ final Transaction txn = db.beginTransaction();
+ db.accountExternalIds().delete(toDelete, txn);
+ txn.commit();
+ accountCache.evict(me);
+ for (AccountExternalId e : toDelete) {
+ byEmailCache.evict(e.getEmailAddress());
+ }
+ }
+
+ return removed;
+ }
+ });
+ }
+
+ public void updateContact(final String name, final String emailAddr,
+ final ContactInformation info, final AsyncCallback<Account> callback) {
+ run(callback, new Action<Account>() {
+ public Account run(ReviewDb db) throws OrmException, Failure {
+ final Account me = db.accounts().get(getAccountId());
+ final String oldEmail = me.getPreferredEmail();
+ if (realm.allowsEdit(Account.FieldName.FULL_NAME)) {
+ me.setFullName(name != null && !name.isEmpty() ? name : null);
+ }
+ me.setPreferredEmail(emailAddr);
+ if (useContactInfo) {
+ if (ContactInformation.hasAddress(info)
+ || (me.isContactFiled() && ContactInformation.hasData(info))) {
+ me.setContactFiled();
+ }
+ if (ContactInformation.hasData(info)) {
+ try {
+ contactStore.store(me, info);
+ } catch (ContactInformationStoreException e) {
+ throw new Failure(e);
+ }
+ }
+ }
+ db.accounts().update(Collections.singleton(me));
+ if (!eq(oldEmail, me.getPreferredEmail())) {
+ byEmailCache.evict(oldEmail);
+ byEmailCache.evict(me.getPreferredEmail());
+ }
+ accountCache.evict(me.getId());
+ return me;
+ }
+ });
+ }
+
+ private static boolean eq(final String a, final String b) {
+ if (a == null && b == null) {
+ return true;
+ }
+ return a != null && a.equals(b);
+ }
+
+ public void enterAgreement(final ContributorAgreement.Id id,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final ContributorAgreement cla = db.contributorAgreements().get(id);
+ if (cla == null || !cla.isActive()) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final AccountAgreement a =
+ new AccountAgreement(new AccountAgreement.Key(getAccountId(), id));
+ if (cla.isAutoVerify()) {
+ a.review(AccountAgreement.Status.VERIFIED, null);
+ }
+ db.accountAgreements().insert(Collections.singleton(a));
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void registerEmail(final String address,
+ final AsyncCallback<VoidResult> cb) {
+ try {
+ final RegisterNewEmailSender sender;
+ sender = registerNewEmailFactory.create(address);
+ sender.send();
+ cb.onSuccess(VoidResult.INSTANCE);
+ } catch (EmailException e) {
+ log.error("Cannot send email verification message to " + address, e);
+ cb.onFailure(e);
+ } catch (RuntimeException e) {
+ log.error("Cannot send email verification message to " + address, e);
+ cb.onFailure(e);
+ }
+ }
+
+ public void validateEmail(final String token,
+ final AsyncCallback<VoidResult> callback) {
+ try {
+ final ValidToken t =
+ authConfig.getEmailRegistrationToken().checkToken(token, null);
+ if (t == null || t.getData() == null || "".equals(t.getData())) {
+ callback.onFailure(new IllegalStateException("Invalid token"));
+ return;
+ }
+ final String newEmail = new String(Base64.decode(t.getData()), "UTF-8");
+ if (!newEmail.contains("@")) {
+ callback.onFailure(new IllegalStateException("Invalid token"));
+ return;
+ }
+ accountManager.link(getAccountId(), AuthRequest.forEmail(newEmail));
+ callback.onSuccess(VoidResult.INSTANCE);
+ } catch (XsrfException e) {
+ callback.onFailure(e);
+ } catch (UnsupportedEncodingException e) {
+ callback.onFailure(e);
+ } catch (AccountException e) {
+ callback.onFailure(e);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
new file mode 100644
index 0000000000..e77023557d
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AccountServiceImpl.java
@@ -0,0 +1,172 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.common.data.AccountProjectWatchInfo;
+import com.google.gerrit.common.data.AccountService;
+import com.google.gerrit.common.data.AgreementInfo;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+class AccountServiceImpl extends BaseServiceImplementation implements
+ AccountService {
+ private final Provider<IdentifiedUser> currentUser;
+ private final AccountCache accountCache;
+ private final ProjectControl.Factory projectControlFactory;
+ private final AgreementInfoFactory.Factory agreementInfoFactory;
+
+ @Inject
+ AccountServiceImpl(final Provider<ReviewDb> schema,
+ final Provider<IdentifiedUser> identifiedUser,
+ final AccountCache accountCache,
+ final ProjectControl.Factory projectControlFactory,
+ final AgreementInfoFactory.Factory agreementInfoFactory) {
+ super(schema, identifiedUser);
+ this.currentUser = identifiedUser;
+ this.accountCache = accountCache;
+ this.projectControlFactory = projectControlFactory;
+ this.agreementInfoFactory = agreementInfoFactory;
+ }
+
+ public void myAccount(final AsyncCallback<Account> callback) {
+ callback.onSuccess(currentUser.get().getAccount());
+ }
+
+ public void changePreferences(final AccountGeneralPreferences pref,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final Account a = db.accounts().get(getAccountId());
+ if (a == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ a.setGeneralPreferences(pref);
+ db.accounts().update(Collections.singleton(a));
+ accountCache.evict(a.getId());
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void myProjectWatch(
+ final AsyncCallback<List<AccountProjectWatchInfo>> callback) {
+ run(callback, new Action<List<AccountProjectWatchInfo>>() {
+ public List<AccountProjectWatchInfo> run(ReviewDb db) throws OrmException {
+ final List<AccountProjectWatchInfo> r =
+ new ArrayList<AccountProjectWatchInfo>();
+
+ for (final AccountProjectWatch w : db.accountProjectWatches()
+ .byAccount(getAccountId()).toList()) {
+ final ProjectControl ctl;
+ try {
+ ctl = projectControlFactory.validateFor(w.getProjectNameKey());
+ } catch (NoSuchProjectException e) {
+ db.accountProjectWatches().delete(Collections.singleton(w));
+ continue;
+ }
+ r.add(new AccountProjectWatchInfo(w, ctl.getProject()));
+ }
+ Collections.sort(r, new Comparator<AccountProjectWatchInfo>() {
+ public int compare(final AccountProjectWatchInfo a,
+ final AccountProjectWatchInfo b) {
+ return a.getProject().getName().compareTo(b.getProject().getName());
+ }
+ });
+ return r;
+ }
+ });
+ }
+
+ public void addProjectWatch(final String projectName,
+ final AsyncCallback<AccountProjectWatchInfo> callback) {
+ run(callback, new Action<AccountProjectWatchInfo>() {
+ public AccountProjectWatchInfo run(ReviewDb db) throws OrmException,
+ NoSuchProjectException {
+ final Project.NameKey nameKey = new Project.NameKey(projectName);
+ final ProjectControl ctl = projectControlFactory.validateFor(nameKey);
+
+ final AccountProjectWatch watch =
+ new AccountProjectWatch(
+ new AccountProjectWatch.Key(((IdentifiedUser) ctl
+ .getCurrentUser()).getAccountId(), nameKey));
+ db.accountProjectWatches().insert(Collections.singleton(watch));
+ return new AccountProjectWatchInfo(watch, ctl.getProject());
+ }
+ });
+ }
+
+ public void updateProjectWatch(final AccountProjectWatch watch,
+ final AsyncCallback<VoidResult> callback) {
+ if (!getAccountId().equals(watch.getAccountId())) {
+ callback.onFailure(new NoSuchEntityException());
+ return;
+ }
+
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(ReviewDb db) throws OrmException {
+ db.accountProjectWatches().update(Collections.singleton(watch));
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void deleteProjectWatches(final Set<AccountProjectWatch.Key> keys,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final Account.Id me = getAccountId();
+ for (final AccountProjectWatch.Key keyId : keys) {
+ if (!me.equals(keyId.getParentKey()))
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final List<AccountProjectWatch> k =
+ db.accountProjectWatches().get(keys).toList();
+ if (!k.isEmpty()) {
+ final Transaction txn = db.beginTransaction();
+ db.accountProjectWatches().delete(k, txn);
+ txn.commit();
+ }
+
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void myAgreements(final AsyncCallback<AgreementInfo> callback) {
+ agreementInfoFactory.create().to(callback);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
new file mode 100644
index 0000000000..8e22741746
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.common.data.AgreementInfo;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupAgreement;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class AgreementInfoFactory extends Handler<AgreementInfo> {
+ interface Factory {
+ AgreementInfoFactory create();
+ }
+
+ private final ReviewDb db;
+ private final IdentifiedUser user;
+
+ private AgreementInfo info;
+
+ @Inject
+ AgreementInfoFactory(final ReviewDb db, final IdentifiedUser user) {
+ this.db = db;
+ this.user = user;
+ }
+
+ @Override
+ public AgreementInfo call() throws Exception {
+ final List<AccountAgreement> userAccepted =
+ db.accountAgreements().byAccount(user.getAccountId()).toList();
+ final List<AccountGroupAgreement> groupAccepted =
+ new ArrayList<AccountGroupAgreement>();
+ for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
+ groupAccepted.addAll(db.accountGroupAgreements().byGroup(groupId)
+ .toList());
+ }
+
+ final Map<ContributorAgreement.Id, ContributorAgreement> agreements =
+ new HashMap<ContributorAgreement.Id, ContributorAgreement>();
+ for (final AccountAgreement a : userAccepted) {
+ final ContributorAgreement.Id id = a.getAgreementId();
+ if (!agreements.containsKey(id)) {
+ agreements.put(id, db.contributorAgreements().get(id));
+ }
+ }
+ for (final AccountGroupAgreement a : groupAccepted) {
+ final ContributorAgreement.Id id = a.getAgreementId();
+ if (!agreements.containsKey(id)) {
+ agreements.put(id, db.contributorAgreements().get(id));
+ }
+ }
+
+ info = new AgreementInfo();
+ info.setUserAccepted(userAccepted);
+ info.setGroupAccepted(groupAccepted);
+ info.setAgreements(agreements);
+ return info;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/ExternalIdDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/ExternalIdDetailFactory.java
new file mode 100644
index 0000000000..dacb9bb49a
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/ExternalIdDetailFactory.java
@@ -0,0 +1,53 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+
+import java.util.Collections;
+import java.util.List;
+
+class ExternalIdDetailFactory extends Handler<List<AccountExternalId>> {
+ interface Factory {
+ ExternalIdDetailFactory create();
+ }
+
+ private final ReviewDb db;
+ private final IdentifiedUser user;
+ private final AuthConfig authConfig;
+
+ @Inject
+ ExternalIdDetailFactory(final ReviewDb db, final IdentifiedUser user,
+ final AuthConfig authConfig) {
+ this.db = db;
+ this.user = user;
+ this.authConfig = authConfig;
+ }
+
+ @Override
+ public List<AccountExternalId> call() throws Exception {
+ final List<AccountExternalId> ids =
+ db.accountExternalIds().byAccount(user.getAccountId()).toList();
+ for (final AccountExternalId e : ids) {
+ e.setTrusted(authConfig.isIdentityTrustable(Collections.singleton(e)));
+ }
+ return ids;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
new file mode 100644
index 0000000000..fa072c1156
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -0,0 +1,363 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.common.data.GroupAdminService;
+import com.google.gerrit.common.data.GroupDetail;
+import com.google.gerrit.common.errors.NameAlreadyUsedException;
+import com.google.gerrit.common.errors.NoSuchAccountException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gerrit.reviewdb.AccountGroupMemberAudit;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.account.NoSuchGroupException;
+import com.google.gerrit.server.account.Realm;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class GroupAdminServiceImpl extends BaseServiceImplementation implements
+ GroupAdminService {
+ private final Provider<IdentifiedUser> identifiedUser;
+ private final AccountCache accountCache;
+ private final AccountResolver accountResolver;
+ private final Realm accountRealm;
+ private final GroupCache groupCache;
+ private final GroupControl.Factory groupControlFactory;
+ private final GroupDetailFactory.Factory groupDetailFactory;
+
+ @Inject
+ GroupAdminServiceImpl(final Provider<ReviewDb> schema,
+ final Provider<IdentifiedUser> currentUser,
+ final AccountCache accountCache, final AccountResolver accountResolver,
+ final Realm accountRealm, final GroupCache groupCache,
+ final GroupControl.Factory groupControlFactory,
+ final GroupDetailFactory.Factory groupDetailFactory) {
+ super(schema, currentUser);
+ this.identifiedUser = currentUser;
+ this.accountCache = accountCache;
+ this.accountResolver = accountResolver;
+ this.accountRealm = accountRealm;
+ this.groupCache = groupCache;
+ this.groupControlFactory = groupControlFactory;
+ this.groupDetailFactory = groupDetailFactory;
+ }
+
+ public void ownedGroups(final AsyncCallback<List<AccountGroup>> callback) {
+ run(callback, new Action<List<AccountGroup>>() {
+ public List<AccountGroup> run(ReviewDb db) throws OrmException {
+ final IdentifiedUser user = identifiedUser.get();
+ final List<AccountGroup> result;
+ if (user.isAdministrator()) {
+ result = db.accountGroups().all().toList();
+ } else {
+ final HashSet<AccountGroup.Id> seen = new HashSet<AccountGroup.Id>();
+ result = new ArrayList<AccountGroup>();
+ for (final AccountGroup.Id myGroup : user.getEffectiveGroups()) {
+ for (AccountGroup group : db.accountGroups().ownedByGroup(myGroup)) {
+ final AccountGroup.Id id = group.getId();
+ if (!seen.add(id)) {
+ continue;
+ }
+ try {
+ GroupControl c = groupControlFactory.controlFor(id);
+ if (c.isOwner()) {
+ result.add(c.getAccountGroup());
+ }
+ } catch (NoSuchGroupException e) {
+ continue;
+ }
+ }
+ }
+ }
+ Collections.sort(result, new Comparator<AccountGroup>() {
+ public int compare(final AccountGroup a, final AccountGroup b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ return result;
+ }
+ });
+ }
+
+ public void createGroup(final String newName,
+ final AsyncCallback<AccountGroup.Id> callback) {
+ run(callback, new Action<AccountGroup.Id>() {
+ public AccountGroup.Id run(final ReviewDb db) throws OrmException,
+ Failure {
+ final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(newName);
+ if (db.accountGroups().get(nameKey) != null) {
+ throw new Failure(new NameAlreadyUsedException());
+ }
+
+ final AccountGroup group =
+ new AccountGroup(nameKey, new AccountGroup.Id(db
+ .nextAccountGroupId()));
+ group.setNameKey(nameKey);
+ group.setType(AccountGroup.Type.INTERNAL);
+ group.setDescription("");
+
+ final Account.Id me = getAccountId();
+ final AccountGroupMember m =
+ new AccountGroupMember(
+ new AccountGroupMember.Key(me, group.getId()));
+
+ final Transaction txn = db.beginTransaction();
+ db.accountGroups().insert(Collections.singleton(group), txn);
+ db.accountGroupMembers().insert(Collections.singleton(m), txn);
+ db.accountGroupMembersAudit().insert(
+ Collections.singleton(new AccountGroupMemberAudit(m, me)), txn);
+ txn.commit();
+ accountCache.evict(m.getAccountId());
+
+ return group.getId();
+ }
+ });
+ }
+
+ public void groupDetail(final AccountGroup.Id groupId,
+ final AsyncCallback<GroupDetail> callback) {
+ groupDetailFactory.create(groupId).to(callback);
+ }
+
+ public void changeGroupDescription(final AccountGroup.Id groupId,
+ final String description, final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ assertAmGroupOwner(db, group);
+ group.setDescription(description);
+ db.accountGroups().update(Collections.singleton(group));
+ groupCache.evict(group);
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void changeGroupOwner(final AccountGroup.Id groupId,
+ final String newOwnerName, final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ assertAmGroupOwner(db, group);
+
+ final AccountGroup owner =
+ db.accountGroups().get(new AccountGroup.NameKey(newOwnerName));
+ if (owner == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ group.setOwnerGroupId(owner.getId());
+ db.accountGroups().update(Collections.singleton(group));
+ groupCache.evict(group);
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void renameGroup(final AccountGroup.Id groupId, final String newName,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ assertAmGroupOwner(db, group);
+
+ final AccountGroup.NameKey oldKey = group.getNameKey();
+ final AccountGroup.NameKey newKey = new AccountGroup.NameKey(newName);
+ if (!newKey.equals(oldKey)) {
+ if (db.accountGroups().get(newKey) != null) {
+ throw new Failure(new NameAlreadyUsedException());
+ }
+ group.setNameKey(newKey);
+ db.accountGroups().update(Collections.singleton(group));
+ groupCache.evict(group);
+ groupCache.evictAfterRename(oldKey);
+ }
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void changeGroupType(final AccountGroup.Id groupId,
+ final AccountGroup.Type newType, final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ assertAmGroupOwner(db, group);
+ group.setType(newType);
+ db.accountGroups().update(Collections.singleton(group));
+ groupCache.evict(group);
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void changeExternalGroup(final AccountGroup.Id groupId,
+ final AccountGroup.ExternalNameKey bindTo,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException, Failure {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ assertAmGroupOwner(db, group);
+ group.setExternalNameKey(bindTo);
+ db.accountGroups().update(Collections.singleton(group));
+ groupCache.evict(group);
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void searchExternalGroups(final String searchFilter,
+ final AsyncCallback<List<AccountGroup.ExternalNameKey>> callback) {
+ final ArrayList<AccountGroup.ExternalNameKey> matches =
+ new ArrayList<AccountGroup.ExternalNameKey>(accountRealm
+ .lookupGroups(searchFilter));
+ Collections.sort(matches, new Comparator<AccountGroup.ExternalNameKey>() {
+ @Override
+ public int compare(AccountGroup.ExternalNameKey a,
+ AccountGroup.ExternalNameKey b) {
+ return a.get().compareTo(b.get());
+ }
+ });
+ callback.onSuccess(matches);
+ }
+
+ public void addGroupMember(final AccountGroup.Id groupId,
+ final String nameOrEmail, final AsyncCallback<GroupDetail> callback) {
+ run(callback, new Action<GroupDetail>() {
+ public GroupDetail run(ReviewDb db) throws OrmException, Failure,
+ NoSuchGroupException {
+ final GroupControl control = groupControlFactory.validateFor(groupId);
+ if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ throw new Failure(new NameAlreadyUsedException());
+ }
+
+ final Account a = findAccount(nameOrEmail);
+ if (!control.canAdd(a.getId())) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ final AccountGroupMember.Key key =
+ new AccountGroupMember.Key(a.getId(), groupId);
+ AccountGroupMember m = db.accountGroupMembers().get(key);
+ if (m == null) {
+ m = new AccountGroupMember(key);
+ final Transaction txn = db.beginTransaction();
+ db.accountGroupMembers().insert(Collections.singleton(m), txn);
+ db.accountGroupMembersAudit().insert(
+ Collections.singleton(new AccountGroupMemberAudit(m,
+ getAccountId())), txn);
+ txn.commit();
+ accountCache.evict(m.getAccountId());
+ }
+
+ return groupDetailFactory.create(groupId).call();
+ }
+ });
+ }
+
+ public void deleteGroupMembers(final AccountGroup.Id groupId,
+ final Set<AccountGroupMember.Key> keys,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(final ReviewDb db) throws OrmException,
+ NoSuchGroupException, Failure {
+ final GroupControl control = groupControlFactory.validateFor(groupId);
+ if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
+ throw new Failure(new NameAlreadyUsedException());
+ }
+
+ for (final AccountGroupMember.Key k : keys) {
+ if (!groupId.equals(k.getAccountGroupId())) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ }
+
+ final Account.Id me = getAccountId();
+ for (final AccountGroupMember.Key k : keys) {
+ final AccountGroupMember m = db.accountGroupMembers().get(k);
+ if (m != null) {
+ if (!control.canRemove(m.getAccountId())) {
+ throw new Failure(new NoSuchEntityException());
+ }
+
+ AccountGroupMemberAudit audit = null;
+ for (AccountGroupMemberAudit a : db.accountGroupMembersAudit()
+ .byGroupAccount(m.getAccountGroupId(), m.getAccountId())) {
+ if (a.isActive()) {
+ audit = a;
+ break;
+ }
+ }
+
+ final Transaction txn = db.beginTransaction();
+ db.accountGroupMembers().delete(Collections.singleton(m), txn);
+ if (audit != null) {
+ audit.removed(me);
+ db.accountGroupMembersAudit().update(
+ Collections.singleton(audit), txn);
+ } else {
+ audit = new AccountGroupMemberAudit(m, me);
+ audit.removedLegacy();
+ db.accountGroupMembersAudit().insert(
+ Collections.singleton(audit), txn);
+ }
+ txn.commit();
+ accountCache.evict(m.getAccountId());
+ }
+ }
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ private void assertAmGroupOwner(final ReviewDb db, final AccountGroup group)
+ throws Failure {
+ try {
+ if (!groupControlFactory.controlFor(group.getId()).isOwner()) {
+ throw new Failure(new NoSuchGroupException(group.getId()));
+ }
+ } catch (NoSuchGroupException e) {
+ throw new Failure(new NoSuchGroupException(group.getId()));
+ }
+ }
+
+ private Account findAccount(final String nameOrEmail) throws OrmException,
+ Failure {
+ final Account r = accountResolver.find(nameOrEmail);
+ if (r == null) {
+ throw new Failure(new NoSuchAccountException(nameOrEmail));
+ }
+ return r;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupDetailFactory.java
new file mode 100644
index 0000000000..1b0566021e
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupDetailFactory.java
@@ -0,0 +1,111 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.common.data.GroupDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.account.NoSuchGroupException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+class GroupDetailFactory extends Handler<GroupDetail> {
+ interface Factory {
+ GroupDetailFactory create(AccountGroup.Id groupId);
+ }
+
+ private final ReviewDb db;
+ private final GroupControl.Factory groupControl;
+ private final GroupCache groupCache;
+ private final AccountInfoCacheFactory aic;
+
+ private final AccountGroup.Id groupId;
+ private GroupControl control;
+
+ @Inject
+ GroupDetailFactory(final ReviewDb db,
+ final GroupControl.Factory groupControl, final GroupCache groupCache,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ @Assisted final AccountGroup.Id groupId) {
+ this.db = db;
+ this.groupControl = groupControl;
+ this.groupCache = groupCache;
+ this.aic = accountInfoCacheFactory.create();
+
+ this.groupId = groupId;
+ }
+
+ @Override
+ public GroupDetail call() throws OrmException, NoSuchGroupException {
+ control = groupControl.validateFor(groupId);
+ final AccountGroup group = control.getAccountGroup();
+ final GroupDetail detail = new GroupDetail();
+ detail.setGroup(group);
+ detail.setOwnerGroup(groupCache.get(group.getOwnerGroupId()));
+ switch (group.getType()) {
+ case INTERNAL:
+ detail.setMembers(loadMembers());
+ break;
+ }
+ detail.setAccounts(aic.create());
+ return detail;
+ }
+
+ private List<AccountGroupMember> loadMembers() throws OrmException {
+ List<AccountGroupMember> members = new ArrayList<AccountGroupMember>();
+ for (final AccountGroupMember m : db.accountGroupMembers().byGroup(groupId)) {
+ if (control.canSee(m.getAccountId())) {
+ aic.want(m.getAccountId());
+ members.add(m);
+ }
+ }
+
+ Collections.sort(members, new Comparator<AccountGroupMember>() {
+ public int compare(final AccountGroupMember o1,
+ final AccountGroupMember o2) {
+ final Account a = aic.get(o1.getAccountId());
+ final Account b = aic.get(o2.getAccountId());
+ return n(a).compareTo(n(b));
+ }
+
+ private String n(final Account a) {
+ String n = a.getFullName();
+ if (n != null && n.length() > 0) {
+ return n;
+ }
+
+ n = a.getPreferredEmail();
+ if (n != null && n.length() > 0) {
+ return n;
+ }
+
+ return a.getId().toString();
+ }
+ });
+ return members;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
new file mode 100644
index 0000000000..b3e993e07d
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.account;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+class MyGroupsFactory extends Handler<List<AccountGroup>> {
+ interface Factory {
+ MyGroupsFactory create();
+ }
+
+ private final GroupCache groupCache;
+ private final IdentifiedUser user;
+
+ @Inject
+ MyGroupsFactory(final GroupCache groupCache, final IdentifiedUser user) {
+ this.groupCache = groupCache;
+ this.user = user;
+ }
+
+ @Override
+ public List<AccountGroup> call() throws Exception {
+ final Set<AccountGroup.Id> effective = user.getEffectiveGroups();
+ final int cnt = effective.size();
+ final List<AccountGroup> groupList = new ArrayList<AccountGroup>(cnt);
+ for (final AccountGroup.Id groupId : effective) {
+ groupList.add(groupCache.get(groupId));
+ }
+ Collections.sort(groupList, new Comparator<AccountGroup>() {
+ @Override
+ public int compare(AccountGroup a, AccountGroup b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ return groupList;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java
new file mode 100644
index 0000000000..ac5a0e7b21
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/AbandonChange.java
@@ -0,0 +1,143 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.OrmRunnable;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+import java.util.List;
+
+class AbandonChange extends Handler<ChangeDetail> {
+ interface Factory {
+ AbandonChange create(PatchSet.Id patchSetId, String message);
+ }
+
+ private final ChangeControl.Factory changeControlFactory;
+ private final ReviewDb db;
+ private final IdentifiedUser currentUser;
+ private final AbandonedSender.Factory abandonedSenderFactory;
+ private final ChangeDetailFactory.Factory changeDetailFactory;
+
+ private final PatchSet.Id patchSetId;
+ @Nullable
+ private final String message;
+
+ @Inject
+ AbandonChange(final ChangeControl.Factory changeControlFactory,
+ final ReviewDb db, final IdentifiedUser currentUser,
+ final AbandonedSender.Factory abandonedSenderFactory,
+ final ChangeDetailFactory.Factory changeDetailFactory,
+ @Assisted final PatchSet.Id patchSetId,
+ @Assisted @Nullable final String message) {
+ this.changeControlFactory = changeControlFactory;
+ this.db = db;
+ this.currentUser = currentUser;
+ this.abandonedSenderFactory = abandonedSenderFactory;
+ this.changeDetailFactory = changeDetailFactory;
+
+ this.patchSetId = patchSetId;
+ this.message = message;
+ }
+
+ @Override
+ public ChangeDetail call() throws NoSuchChangeException, OrmException,
+ EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException {
+ final Change.Id changeId = patchSetId.getParentKey();
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ if (!control.canAbandon()) {
+ throw new NoSuchChangeException(changeId);
+ }
+ final Change change = control.getChange();
+ final PatchSet patch = db.patchSets().get(patchSetId);
+ if (patch == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ final ChangeMessage cmsg =
+ new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
+ .messageUUID(db)), currentUser.getAccountId());
+ final StringBuilder msgBuf =
+ new StringBuilder("Patch Set " + change.currentPatchSetId().get()
+ + ": Abandoned");
+ if (message != null && message.length() > 0) {
+ msgBuf.append("\n\n");
+ msgBuf.append(message);
+ }
+ cmsg.setMessage(msgBuf.toString());
+
+ Boolean dbSuccess = db.run(new OrmRunnable<Boolean, ReviewDb>() {
+ public Boolean run(ReviewDb db, Transaction txn, boolean retry)
+ throws OrmException {
+ return doAbandonChange(message, change, patchSetId, cmsg, db, txn);
+ }
+ });
+
+ if (dbSuccess) {
+ // Email the reviewers
+ final AbandonedSender cm = abandonedSenderFactory.create(change);
+ cm.setFrom(currentUser.getAccountId());
+ cm.setReviewDb(db);
+ cm.setChangeMessage(cmsg);
+ cm.send();
+ }
+
+ return changeDetailFactory.create(changeId).call();
+ }
+
+ private Boolean doAbandonChange(final String message, final Change change,
+ final PatchSet.Id psid, final ChangeMessage cm, final ReviewDb db,
+ final Transaction txn) throws OrmException {
+
+ // Check to make sure the change status and current patchset ID haven't
+ // changed while the user was typing an abandon message
+ if (change.getStatus().isOpen() && change.currentPatchSetId().equals(psid)) {
+ change.setStatus(Change.Status.ABANDONED);
+ ChangeUtil.updated(change);
+
+ final List<PatchSetApproval> approvals =
+ db.patchSetApprovals().byChange(change.getId()).toList();
+ for (PatchSetApproval a : approvals) {
+ a.cache(change);
+ }
+ db.patchSetApprovals().update(approvals, txn);
+
+ db.changeMessages().insert(Collections.singleton(cm), txn);
+ db.changes().update(Collections.singleton(change), txn);
+ return Boolean.TRUE;
+ }
+
+ return Boolean.FALSE;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
new file mode 100644
index 0000000000..102b41c1d5
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -0,0 +1,240 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.ApprovalDetail;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeInfo;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetAncestor;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.workflow.CategoryFunction;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Creates a {@link ChangeDetail} from a {@link Change}. */
+public class ChangeDetailFactory extends Handler<ChangeDetail> {
+ public interface Factory {
+ ChangeDetailFactory create(Change.Id id);
+ }
+
+ private final ApprovalTypes approvalTypes;
+ private final ChangeControl.Factory changeControlFactory;
+ private final FunctionState.Factory functionState;
+ private final PatchSetDetailFactory.Factory patchSetDetail;
+ private final AccountInfoCacheFactory aic;
+ private final ReviewDb db;
+
+ private final Change.Id changeId;
+
+ private ChangeDetail detail;
+ private ChangeControl control;
+
+ @Inject
+ ChangeDetailFactory(final ApprovalTypes approvalTypes,
+ final FunctionState.Factory functionState,
+ final PatchSetDetailFactory.Factory patchSetDetail, final ReviewDb db,
+ final ChangeControl.Factory changeControlFactory,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ @Assisted final Change.Id id) {
+ this.approvalTypes = approvalTypes;
+ this.functionState = functionState;
+ this.patchSetDetail = patchSetDetail;
+ this.db = db;
+ this.changeControlFactory = changeControlFactory;
+ this.aic = accountInfoCacheFactory.create();
+
+ this.changeId = id;
+ }
+
+ @Override
+ public ChangeDetail call() throws OrmException, NoSuchEntityException,
+ PatchSetInfoNotAvailableException, NoSuchChangeException {
+ control = changeControlFactory.validateFor(changeId);
+ final Change change = control.getChange();
+ final Project proj = control.getProject();
+ final PatchSet patch = db.patchSets().get(change.currentPatchSetId());
+ if (patch == null) {
+ throw new NoSuchEntityException();
+ }
+
+ aic.want(change.getOwner());
+
+ detail = new ChangeDetail();
+ detail.setChange(change);
+ detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
+ detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
+ detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
+ changeId));
+ loadPatchSets();
+ loadMessages();
+ if (change.currentPatchSetId() != null) {
+ loadCurrentPatchSet();
+ }
+ load();
+ detail.setAccounts(aic.create());
+ return detail;
+ }
+
+ private void loadPatchSets() throws OrmException {
+ detail.setPatchSets(db.patchSets().byChange(changeId).toList());
+ }
+
+ private void loadMessages() throws OrmException {
+ detail.setMessages(db.changeMessages().byChange(changeId).toList());
+ for (final ChangeMessage m : detail.getMessages()) {
+ aic.want(m.getAuthor());
+ }
+ }
+
+ private void load() throws OrmException {
+ final PatchSet.Id psId = detail.getChange().currentPatchSetId();
+ final List<PatchSetApproval> allApprovals =
+ db.patchSetApprovals().byChange(changeId).toList();
+
+ if (detail.getChange().getStatus().isOpen()) {
+ final FunctionState fs =
+ functionState.create(detail.getChange(), psId, allApprovals);
+
+ final Set<ApprovalCategory.Id> missingApprovals =
+ new HashSet<ApprovalCategory.Id>();
+
+ final Set<ApprovalCategory.Id> currentActions =
+ new HashSet<ApprovalCategory.Id>();
+
+ for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
+ CategoryFunction.forCategory(at.getCategory()).run(at, fs);
+ if (!fs.isValid(at)) {
+ missingApprovals.add(at.getCategory().getId());
+ }
+ }
+ for (final ApprovalType at : approvalTypes.getActionTypes()) {
+ if (CategoryFunction.forCategory(at.getCategory()).isValid(
+ control.getCurrentUser(), at, fs)) {
+ currentActions.add(at.getCategory().getId());
+ }
+ }
+ detail.setMissingApprovals(missingApprovals);
+ detail.setCurrentActions(currentActions);
+ }
+
+ final HashMap<Account.Id, ApprovalDetail> ad =
+ new HashMap<Account.Id, ApprovalDetail>();
+ for (PatchSetApproval ca : allApprovals) {
+ ApprovalDetail d = ad.get(ca.getAccountId());
+ if (d == null) {
+ d = new ApprovalDetail(ca.getAccountId());
+ ad.put(d.getAccount(), d);
+ }
+ if (ca.getPatchSetId().equals(psId)) {
+ d.add(ca);
+ }
+ }
+
+ final Account.Id owner = detail.getChange().getOwner();
+ if (ad.containsKey(owner)) {
+ // Ensure the owner always sorts to the top of the table
+ //
+ ad.get(owner).sortFirst();
+ }
+
+ aic.want(ad.keySet());
+ detail.setApprovals(ad.values());
+ }
+
+ private void loadCurrentPatchSet() throws OrmException,
+ NoSuchEntityException, PatchSetInfoNotAvailableException,
+ NoSuchChangeException {
+ final PatchSet.Id psId = detail.getChange().currentPatchSetId();
+ final PatchSetDetailFactory loader = patchSetDetail.create(psId);
+ loader.patchSet = detail.getCurrentPatchSet();
+ loader.control = control;
+ detail.setCurrentPatchSetDetail(loader.call());
+
+ final HashSet<Change.Id> changesToGet = new HashSet<Change.Id>();
+ final List<Change.Id> ancestorOrder = new ArrayList<Change.Id>();
+ for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(psId)) {
+ for (PatchSet p : db.patchSets().byRevision(a.getAncestorRevision())) {
+ final Change.Id ck = p.getId().getParentKey();
+ if (changesToGet.add(ck)) {
+ ancestorOrder.add(ck);
+ }
+ }
+ }
+
+ final RevId cprev = loader.patchSet.getRevision();
+ final List<PatchSetAncestor> descendants =
+ cprev != null ? db.patchSetAncestors().descendantsOf(cprev).toList()
+ : Collections.<PatchSetAncestor> emptyList();
+ for (final PatchSetAncestor a : descendants) {
+ changesToGet.add(a.getPatchSet().getParentKey());
+ }
+ final Map<Change.Id, Change> m =
+ db.changes().toMap(db.changes().get(changesToGet));
+
+ final ArrayList<ChangeInfo> dependsOn = new ArrayList<ChangeInfo>();
+ for (final Change.Id a : ancestorOrder) {
+ final Change ac = m.get(a);
+ if (ac != null) {
+ aic.want(ac.getOwner());
+ dependsOn.add(new ChangeInfo(ac));
+ }
+ }
+
+ final ArrayList<ChangeInfo> neededBy = new ArrayList<ChangeInfo>();
+ for (final PatchSetAncestor a : descendants) {
+ final Change ac = m.get(a.getPatchSet().getParentKey());
+ if (ac != null) {
+ aic.want(ac.getOwner());
+ neededBy.add(new ChangeInfo(ac));
+ }
+ }
+
+ Collections.sort(neededBy, new Comparator<ChangeInfo>() {
+ public int compare(final ChangeInfo o1, final ChangeInfo o2) {
+ return o1.getId().get() - o2.getId().get();
+ }
+ });
+
+ detail.setDependsOn(dependsOn);
+ detail.setNeededBy(neededBy);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
new file mode 100644
index 0000000000..12fc41d5fc
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailServiceImpl.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeDetailService;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.inject.Inject;
+
+class ChangeDetailServiceImpl implements ChangeDetailService {
+ private final ChangeDetailFactory.Factory changeDetail;
+ private final PatchSetDetailFactory.Factory patchSetDetail;
+ private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail;
+
+ @Inject
+ ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail,
+ final PatchSetDetailFactory.Factory patchSetDetail,
+ final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) {
+ this.changeDetail = changeDetail;
+ this.patchSetDetail = patchSetDetail;
+ this.patchSetPublishDetail = patchSetPublishDetail;
+ }
+
+ public void changeDetail(final Change.Id id,
+ final AsyncCallback<ChangeDetail> callback) {
+ changeDetail.create(id).to(callback);
+ }
+
+ public void patchSetDetail(final PatchSet.Id id,
+ final AsyncCallback<PatchSetDetail> callback) {
+ patchSetDetail.create(id).to(callback);
+ }
+
+ public void patchSetPublishDetail(final PatchSet.Id id,
+ final AsyncCallback<PatchSetPublishDetail> callback) {
+ patchSetPublishDetail.create(id).to(callback);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
new file mode 100644
index 0000000000..196d400de6
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeManageServiceImpl.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.data.ChangeManageService;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.inject.Inject;
+
+class ChangeManageServiceImpl implements ChangeManageService {
+ private final SubmitAction.Factory submitAction;
+ private final AbandonChange.Factory abandonChangeFactory;
+
+ @Inject
+ ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
+ final AbandonChange.Factory abandonChangeFactory) {
+ this.submitAction = patchSetAction;
+ this.abandonChangeFactory = abandonChangeFactory;
+ }
+
+ public void submit(final PatchSet.Id patchSetId,
+ final AsyncCallback<ChangeDetail> cb) {
+ submitAction.create(patchSetId).to(cb);
+ }
+
+ public void abandonChange(final PatchSet.Id patchSetId, final String message,
+ final AsyncCallback<ChangeDetail> callback) {
+ abandonChangeFactory.create(patchSetId, message).to(callback);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
new file mode 100644
index 0000000000..211f505337
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeModule.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.gerrit.server.config.FactoryModule;
+
+public class ChangeModule extends RpcServletModule {
+ public ChangeModule() {
+ super(UiRpcModule.PREFIX);
+ }
+
+ @Override
+ protected void configureServlets() {
+ install(new FactoryModule() {
+ @Override
+ protected void configure() {
+ factory(AbandonChange.Factory.class);
+ factory(ChangeDetailFactory.Factory.class);
+ factory(PatchSetDetailFactory.Factory.class);
+ factory(PatchSetPublishDetailFactory.Factory.class);
+ factory(SubmitAction.Factory.class);
+ }
+ });
+ rpc(ChangeDetailServiceImpl.class);
+ rpc(ChangeManageServiceImpl.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
new file mode 100644
index 0000000000..6898df3ddf
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
@@ -0,0 +1,127 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountPatchReview;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Creates a {@link PatchSetDetail} from a {@link PatchSet}. */
+class PatchSetDetailFactory extends Handler<PatchSetDetail> {
+ interface Factory {
+ PatchSetDetailFactory create(PatchSet.Id id);
+ }
+
+ private final PatchSetInfoFactory infoFactory;
+ private final ReviewDb db;
+ private final PatchListCache patchListCache;
+ private final ChangeControl.Factory changeControlFactory;
+
+ private final PatchSet.Id psId;
+
+ private PatchSetDetail detail;
+ ChangeControl control;
+ PatchSet patchSet;
+
+ @Inject
+ PatchSetDetailFactory(final PatchSetInfoFactory psif, final ReviewDb db,
+ final PatchListCache patchListCache,
+ final ChangeControl.Factory changeControlFactory,
+ @Assisted final PatchSet.Id id) {
+ this.infoFactory = psif;
+ this.db = db;
+ this.patchListCache = patchListCache;
+ this.changeControlFactory = changeControlFactory;
+
+ this.psId = id;
+ }
+
+ @Override
+ public PatchSetDetail call() throws OrmException, NoSuchEntityException,
+ PatchSetInfoNotAvailableException, NoSuchChangeException {
+ if (control == null || patchSet == null) {
+ control = changeControlFactory.validateFor(psId.getParentKey());
+ patchSet = db.patchSets().get(psId);
+ if (patchSet == null) {
+ throw new NoSuchEntityException();
+ }
+ }
+
+ final PatchList list = patchListCache.get(control.getChange(), patchSet);
+ final List<Patch> patches = list.toPatchList(patchSet.getId());
+ final Map<Patch.Key, Patch> byKey = new HashMap<Patch.Key, Patch>();
+ for (final Patch p : patches) {
+ byKey.put(p.getKey(), p);
+ }
+
+ for (final PatchLineComment c : db.patchComments().published(psId)) {
+ final Patch p = byKey.get(c.getKey().getParentKey());
+ if (p != null) {
+ p.setCommentCount(p.getCommentCount() + 1);
+ }
+ }
+
+ detail = new PatchSetDetail();
+ detail.setPatchSet(patchSet);
+
+ detail.setInfo(infoFactory.get(psId));
+ detail.setPatches(patches);
+
+ final CurrentUser user = control.getCurrentUser();
+ if (user instanceof IdentifiedUser) {
+ // If we are signed in, compute the number of draft comments by the
+ // current user on each of these patch files. This way they can more
+ // quickly locate where they have pending drafts, and review them.
+ //
+ final Account.Id me = ((IdentifiedUser) user).getAccountId();
+ for (final PatchLineComment c : db.patchComments().draft(psId, me)) {
+ final Patch p = byKey.get(c.getKey().getParentKey());
+ if (p != null) {
+ p.setDraftCount(p.getDraftCount() + 1);
+ }
+ }
+
+ for (AccountPatchReview r : db.accountPatchReviews().byReviewer(me, psId)) {
+ final Patch p = byKey.get(r.getKey().getPatchKey());
+ if (p != null) {
+ p.setReviewedByCurrentUser(true);
+ }
+ }
+ }
+
+ return detail;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
new file mode 100644
index 0000000000..ca12b8fa36
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
@@ -0,0 +1,155 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.PatchSetPublishDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> {
+ interface Factory {
+ PatchSetPublishDetailFactory create(PatchSet.Id patchSetId);
+ }
+
+ private final ProjectCache projectCache;
+ private final PatchSetInfoFactory infoFactory;
+ private final ApprovalTypes approvalTypes;
+ private final ReviewDb db;
+ private final ChangeControl.Factory changeControlFactory;
+ private final AccountInfoCacheFactory aic;
+ private final IdentifiedUser user;
+
+ private final PatchSet.Id patchSetId;
+
+ private AccountInfoCache accounts;
+ private PatchSetInfo patchSetInfo;
+ private Change change;
+ private List<PatchLineComment> drafts;
+ private Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
+ private Map<ApprovalCategory.Id, PatchSetApproval> given;
+
+ @Inject
+ PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory,
+ final ProjectCache projectCache, final ApprovalTypes approvalTypes,
+ final ReviewDb db,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ final ChangeControl.Factory changeControlFactory,
+ final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) {
+ this.projectCache = projectCache;
+ this.infoFactory = infoFactory;
+ this.approvalTypes = approvalTypes;
+ this.db = db;
+ this.changeControlFactory = changeControlFactory;
+ this.aic = accountInfoCacheFactory.create();
+ this.user = user;
+
+ this.patchSetId = patchSetId;
+ }
+
+ @Override
+ public PatchSetPublishDetail call() throws OrmException,
+ PatchSetInfoNotAvailableException, NoSuchChangeException {
+ final Change.Id changeId = patchSetId.getParentKey();
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ change = control.getChange();
+ patchSetInfo = infoFactory.get(patchSetId);
+ drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
+
+ allowed = new HashMap<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>>();
+ given = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ if (change.getStatus().isOpen()
+ && patchSetId.equals(change.currentPatchSetId())) {
+ computeAllowed();
+ for (final PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(
+ patchSetId, user.getAccountId())) {
+ given.put(a.getCategoryId(), a);
+ }
+ }
+
+ aic.want(change.getOwner());
+ accounts = aic.create();
+
+ PatchSetPublishDetail detail = new PatchSetPublishDetail();
+ detail.setAccounts(accounts);
+ detail.setPatchSetInfo(patchSetInfo);
+ detail.setChange(change);
+ detail.setDrafts(drafts);
+ detail.setAllowed(allowed);
+ detail.setGiven(given);
+
+ return detail;
+ }
+
+ private void computeAllowed() {
+ final Set<AccountGroup.Id> am = user.getEffectiveGroups();
+ final ProjectState pe = projectCache.get(change.getProject());
+ computeAllowed(am, pe.getLocalRights());
+ computeAllowed(am, pe.getInheritedRights());
+ }
+
+ private void computeAllowed(final Set<AccountGroup.Id> am,
+ final Collection<ProjectRight> list) {
+ for (final ProjectRight r : list) {
+ if (!am.contains(r.getAccountGroupId())) {
+ continue;
+ }
+
+ Set<ApprovalCategoryValue.Id> s = allowed.get(r.getApprovalCategoryId());
+ if (s == null) {
+ s = new HashSet<ApprovalCategoryValue.Id>();
+ allowed.put(r.getApprovalCategoryId(), s);
+ }
+
+ final ApprovalType at =
+ approvalTypes.getApprovalType(r.getApprovalCategoryId());
+ for (short m = r.getMinValue(); m <= r.getMaxValue(); m++) {
+ final ApprovalCategoryValue v = at.getValue(m);
+ if (v != null) {
+ s.add(v.getId());
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java
new file mode 100644
index 0000000000..a9e48c46d6
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/SubmitAction.java
@@ -0,0 +1,158 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.changedetail;
+
+import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.ChangeDetail;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.MergeQueue;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.workflow.CategoryFunction;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class SubmitAction extends Handler<ChangeDetail> {
+ interface Factory {
+ SubmitAction create(PatchSet.Id patchSetId);
+ }
+
+ private final ReviewDb db;
+ private final MergeQueue merger;
+ private final ApprovalTypes approvalTypes;
+ private final FunctionState.Factory functionState;
+ private final IdentifiedUser user;
+ private final ChangeDetailFactory.Factory changeDetailFactory;
+
+ private final PatchSet.Id patchSetId;
+
+ @Inject
+ SubmitAction(final ReviewDb db, final MergeQueue mq, final ApprovalTypes at,
+ final FunctionState.Factory fs, final IdentifiedUser user,
+ final ChangeDetailFactory.Factory changeDetailFactory,
+ @Assisted final PatchSet.Id patchSetId) {
+ this.db = db;
+ this.merger = mq;
+ this.approvalTypes = at;
+ this.functionState = fs;
+ this.user = user;
+ this.changeDetailFactory = changeDetailFactory;
+
+ this.patchSetId = patchSetId;
+ }
+
+ @Override
+ public ChangeDetail call() throws OrmException, NoSuchEntityException,
+ IllegalStateException, PatchSetInfoNotAvailableException,
+ NoSuchChangeException {
+ final Change change = db.changes().get(patchSetId.getParentKey());
+ if (change == null) {
+ throw new NoSuchEntityException();
+ }
+
+ if (!patchSetId.equals(change.currentPatchSetId())) {
+ throw new IllegalStateException("Patch set " + patchSetId
+ + " not current");
+ }
+ if (change.getStatus().isClosed()) {
+ throw new IllegalStateException("Change" + change.getId() + " is closed");
+ }
+
+ final List<PatchSetApproval> allApprovals =
+ new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
+ patchSetId).toList());
+
+ final PatchSetApproval.Key ak =
+ new PatchSetApproval.Key(patchSetId, user.getAccountId(), SUBMIT);
+ PatchSetApproval myAction = null;
+ boolean isnew = true;
+ for (final PatchSetApproval ca : allApprovals) {
+ if (ak.equals(ca.getKey())) {
+ isnew = false;
+ myAction = ca;
+ myAction.setValue((short) 1);
+ myAction.setGranted();
+ break;
+ }
+ }
+ if (myAction == null) {
+ myAction = new PatchSetApproval(ak, (short) 1);
+ allApprovals.add(myAction);
+ }
+
+ final ApprovalType actionType =
+ approvalTypes.getApprovalType(myAction.getCategoryId());
+ if (actionType == null || !actionType.getCategory().isAction()) {
+ throw new IllegalArgumentException(actionType.getCategory().getName()
+ + " not an action");
+ }
+
+ final FunctionState fs =
+ functionState.create(change, patchSetId, allApprovals);
+ for (ApprovalType c : approvalTypes.getApprovalTypes()) {
+ CategoryFunction.forCategory(c.getCategory()).run(c, fs);
+ }
+ if (!CategoryFunction.forCategory(actionType.getCategory()).isValid(user,
+ actionType, fs)) {
+ throw new IllegalStateException(actionType.getCategory().getName()
+ + " not permitted");
+ }
+ fs.normalize(actionType, myAction);
+ if (myAction.getValue() <= 0) {
+ throw new IllegalStateException(actionType.getCategory().getName()
+ + " not permitted");
+ }
+
+ if (change.getStatus() == Change.Status.NEW) {
+ change.setStatus(Change.Status.SUBMITTED);
+ ChangeUtil.updated(change);
+ }
+
+ final Transaction txn = db.beginTransaction();
+ db.changes().update(Collections.singleton(change), txn);
+ if (change.getStatus().isClosed()) {
+ db.patchSetApprovals().update(fs.getDirtyChangeApprovals(), txn);
+ }
+ if (isnew) {
+ db.patchSetApprovals().insert(Collections.singleton(myAction), txn);
+ } else {
+ db.patchSetApprovals().update(Collections.singleton(myAction), txn);
+ }
+ txn.commit();
+
+ if (change.getStatus() == Change.Status.SUBMITTED) {
+ merger.merge(change.getDest());
+ }
+
+ return changeDetailFactory.create(change.getId()).call();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/AddReviewer.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/AddReviewer.java
new file mode 100644
index 0000000000..f57c08c65d
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/AddReviewer.java
@@ -0,0 +1,158 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.common.data.AddReviewerResult;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.httpd.rpc.changedetail.ChangeDetailFactory;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.mail.AddReviewerSender;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.OrmRunnable;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+class AddReviewer extends Handler<AddReviewerResult> {
+ interface Factory {
+ AddReviewer create(Change.Id changeId, Collection<String> nameOrEmails);
+ }
+
+ private final AddReviewerSender.Factory addReviewerSenderFactory;
+ private final AccountResolver accountResolver;
+ private final ChangeControl.Factory changeControlFactory;
+ private final ChangeDetailFactory.Factory changeDetailFactory;
+ private final ReviewDb db;
+ private final IdentifiedUser currentUser;
+ private final IdentifiedUser.GenericFactory identifiedUserFactory;
+ private final ApprovalCategory.Id addReviewerCategoryId;
+
+ private final Change.Id changeId;
+ private final Collection<String> reviewers;
+
+ @Inject
+ AddReviewer(final AddReviewerSender.Factory addReviewerSenderFactory,
+ final AccountResolver accountResolver,
+ final ChangeControl.Factory changeControlFactory, final ReviewDb db,
+ final IdentifiedUser.GenericFactory identifiedUserFactory,
+ final IdentifiedUser currentUser, final ApprovalTypes approvalTypes,
+ final ChangeDetailFactory.Factory changeDetailFactory,
+ @Assisted final Change.Id changeId,
+ @Assisted final Collection<String> nameOrEmails) {
+ this.addReviewerSenderFactory = addReviewerSenderFactory;
+ this.accountResolver = accountResolver;
+ this.db = db;
+ this.changeControlFactory = changeControlFactory;
+ this.identifiedUserFactory = identifiedUserFactory;
+ this.currentUser = currentUser;
+ this.changeDetailFactory = changeDetailFactory;
+
+ final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
+ addReviewerCategoryId =
+ allTypes.get(allTypes.size() - 1).getCategory().getId();
+
+ this.changeId = changeId;
+ this.reviewers = nameOrEmails;
+ }
+
+ @Override
+ public AddReviewerResult call() throws Exception {
+ final Set<Account.Id> reviewerIds = new HashSet<Account.Id>();
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+
+ final AddReviewerResult result = new AddReviewerResult();
+ for (final String nameOrEmail : reviewers) {
+ final Account account = accountResolver.find(nameOrEmail);
+ if (account == null) {
+ result.addError(new AddReviewerResult.Error(
+ AddReviewerResult.Error.Type.ACCOUNT_NOT_FOUND, nameOrEmail));
+ continue;
+ }
+
+ final IdentifiedUser user = identifiedUserFactory.create(account.getId());
+ if (!control.forUser(user).isVisible()) {
+ result.addError(new AddReviewerResult.Error(
+ AddReviewerResult.Error.Type.CHANGE_NOT_VISIBLE, nameOrEmail));
+ continue;
+ }
+
+ reviewerIds.add(account.getId());
+ }
+
+ if (reviewerIds.isEmpty()) {
+ return result;
+ }
+
+ // Add the reviewers to the database
+ //
+ final Set<Account.Id> added = new HashSet<Account.Id>();
+ db.run(new OrmRunnable<Object, ReviewDb>() {
+ public Object run(ReviewDb db, Transaction txn, boolean retry)
+ throws OrmException {
+ final PatchSet.Id psid = control.getChange().currentPatchSetId();
+ for (final Account.Id reviewer : reviewerIds) {
+ if (!exists(psid, reviewer)) {
+ // This reviewer has not entered an approval for this change yet.
+ //
+ final PatchSetApproval myca = dummyApproval(psid, reviewer);
+ db.patchSetApprovals().insert(Collections.singleton(myca), txn);
+ added.add(reviewer);
+ }
+ }
+ return null;
+ }
+ });
+
+ // Email the reviewers
+ //
+ final AddReviewerSender cm;
+ cm = addReviewerSenderFactory.create(control.getChange());
+ cm.setFrom(currentUser.getAccountId());
+ cm.setReviewDb(db);
+ cm.addReviewers(added);
+ cm.send();
+
+ result.setChange(changeDetailFactory.create(changeId).call());
+ return result;
+ }
+
+ private boolean exists(final PatchSet.Id patchSetId,
+ final Account.Id reviewerId) throws OrmException {
+ return db.patchSetApprovals().byPatchSetUser(patchSetId, reviewerId)
+ .iterator().hasNext();
+ }
+
+ private PatchSetApproval dummyApproval(final PatchSet.Id patchSetId,
+ final Account.Id reviewerId) {
+ return new PatchSetApproval(new PatchSetApproval.Key(patchSetId,
+ reviewerId, addReviewerCategoryId), (short) 0);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/CommentDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/CommentDetailFactory.java
new file mode 100644
index 0000000000..ae529aa77b
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/CommentDetailFactory.java
@@ -0,0 +1,134 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+class CommentDetailFactory extends Handler<CommentDetail> {
+ interface Factory {
+ CommentDetailFactory create(Patch.Key patchKey,
+ @Assisted("patchSetA") PatchSet.Id patchSetA,
+ @Assisted("patchSetB") PatchSet.Id patchSetB);
+ }
+
+ private final ReviewDb db;
+ private final ChangeControl.Factory changeControlFactory;
+ private final AccountInfoCacheFactory aic;
+
+ private final Patch.Key patchKey;
+ private final PatchSet.Id psa;
+ private final PatchSet.Id psb;
+
+ private final PatchSet.Id patchSetId;
+ private final Change.Id changeId;
+
+ @Inject
+ CommentDetailFactory(final ReviewDb db,
+ final ChangeControl.Factory changeControlFactory,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ @Assisted final Patch.Key patchKey,
+ @Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
+ @Assisted("patchSetB") final PatchSet.Id patchSetB) {
+ this.db = db;
+ this.changeControlFactory = changeControlFactory;
+ this.aic = accountInfoCacheFactory.create();
+
+ this.patchKey = patchKey;
+ this.psa = patchSetA;
+ this.psb = patchSetB;
+
+ patchSetId = patchKey.getParentKey();
+ changeId = patchSetId.getParentKey();
+ }
+
+ @Override
+ public CommentDetail call() throws OrmException, NoSuchChangeException {
+ validatePatchSetId(psa);
+ validatePatchSetId(psb);
+
+ final ChangeControl control = changeControlFactory.validateFor(changeId);
+ final String pn = patchKey.getFileName();
+ final CommentDetail r = new CommentDetail(psa, psb);
+
+ final List<Patch> historyList = new ArrayList<Patch>();
+ final Map<PatchSet.Id, Patch> bySet = new HashMap<PatchSet.Id, Patch>();
+ for (final PatchSet ps : db.patchSets().byChange(changeId)) {
+ final Patch p = new Patch(new Patch.Key(ps.getId(), pn));
+ historyList.add(p);
+ bySet.put(ps.getId(), p);
+ }
+
+ for (PatchLineComment c : db.patchComments().published(changeId, pn)) {
+ if (r.include(c)) {
+ aic.want(c.getAuthor());
+ }
+ final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
+ final Patch patch = bySet.get(psId);
+ if (patch != null) {
+ patch.setCommentCount(patch.getCommentCount() + 1);
+ }
+ }
+
+ final CurrentUser user = control.getCurrentUser();
+ if (user instanceof IdentifiedUser) {
+ final Account.Id me = ((IdentifiedUser) user).getAccountId();
+ for (PatchLineComment c : db.patchComments().draft(changeId, pn, me)) {
+ if (r.include(c)) {
+ aic.want(me);
+ }
+ final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
+ final Patch patch = bySet.get(psId);
+ if (patch != null) {
+ patch.setDraftCount(patch.getDraftCount() + 1);
+ }
+ }
+ }
+
+ r.setHistory(historyList);
+ r.setAccountInfoCache(aic.create());
+ return r;
+ }
+
+ private void validatePatchSetId(final PatchSet.Id psId)
+ throws NoSuchChangeException {
+ if (psId == null) { // OK, means use base;
+ } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
+ } else {
+ throw new NoSuchChangeException(changeId);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
new file mode 100644
index 0000000000..fb65e2a165
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
@@ -0,0 +1,419 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.common.data.AddReviewerResult;
+import com.google.gerrit.common.data.ApprovalSummary;
+import com.google.gerrit.common.data.ApprovalSummarySet;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.PatchDetailService;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchScriptSettings;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.httpd.rpc.BaseServiceImplementation;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountPatchReview;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.Patch.Key;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.mail.CommentSender;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.OrmRunnable;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class PatchDetailServiceImpl extends BaseServiceImplementation implements
+ PatchDetailService {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final CommentSender.Factory commentSenderFactory;
+ private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ApprovalTypes approvalTypes;
+
+ private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
+ private final AddReviewer.Factory addReviewerFactory;
+ private final ChangeControl.Factory changeControlFactory;
+ private final CommentDetailFactory.Factory commentDetailFactory;
+ private final FunctionState.Factory functionStateFactory;
+ private final PatchScriptFactory.Factory patchScriptFactoryFactory;
+ private final SaveDraft.Factory saveDraftFactory;
+
+ @Inject
+ PatchDetailServiceImpl(final Provider<ReviewDb> schema,
+ final Provider<CurrentUser> currentUser,
+ final CommentSender.Factory commentSenderFactory,
+ final PatchSetInfoFactory patchSetInfoFactory,
+ final ApprovalTypes approvalTypes,
+ final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
+ final AddReviewer.Factory addReviewerFactory,
+ final ChangeControl.Factory changeControlFactory,
+ final CommentDetailFactory.Factory commentDetailFactory,
+ final FunctionState.Factory functionStateFactory,
+ final PatchScriptFactory.Factory patchScriptFactoryFactory,
+ final SaveDraft.Factory saveDraftFactory) {
+ super(schema, currentUser);
+ this.patchSetInfoFactory = patchSetInfoFactory;
+ this.commentSenderFactory = commentSenderFactory;
+ this.approvalTypes = approvalTypes;
+
+ this.accountInfoCacheFactory = accountInfoCacheFactory;
+ this.addReviewerFactory = addReviewerFactory;
+ this.changeControlFactory = changeControlFactory;
+ this.commentDetailFactory = commentDetailFactory;
+ this.functionStateFactory = functionStateFactory;
+ this.patchScriptFactoryFactory = patchScriptFactoryFactory;
+ this.saveDraftFactory = saveDraftFactory;
+ }
+
+ public void patchScript(final Patch.Key patchKey, final PatchSet.Id psa,
+ final PatchSet.Id psb, final PatchScriptSettings s,
+ final AsyncCallback<PatchScript> callback) {
+ if (psb == null) {
+ callback.onFailure(new NoSuchEntityException());
+ return;
+ }
+ patchScriptFactoryFactory.create(patchKey, psa, psb, s).to(callback);
+ }
+
+ public void patchComments(final Patch.Key patchKey, final PatchSet.Id psa,
+ final PatchSet.Id psb, final AsyncCallback<CommentDetail> callback) {
+ if (psb == null) {
+ callback.onFailure(new NoSuchEntityException());
+ return;
+ }
+ commentDetailFactory.create(patchKey, psa, psb).to(callback);
+ }
+
+ public void saveDraft(final PatchLineComment comment,
+ final AsyncCallback<PatchLineComment> callback) {
+ saveDraftFactory.create(comment).to(callback);
+ }
+
+ public void deleteDraft(final PatchLineComment.Key commentKey,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(ReviewDb db) throws OrmException, Failure {
+ final PatchLineComment comment = db.patchComments().get(commentKey);
+ if (comment == null) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ if (!getAccountId().equals(comment.getAuthor())) {
+ throw new Failure(new NoSuchEntityException());
+ }
+ if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
+ throw new Failure(new IllegalStateException("Comment published"));
+ }
+ db.patchComments().delete(Collections.singleton(comment));
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ public void publishComments(final PatchSet.Id psid, final String message,
+ final Set<ApprovalCategoryValue.Id> approvals,
+ final AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(ReviewDb db) throws OrmException, Failure {
+ final PublishResult r;
+
+ r = db.run(new OrmRunnable<PublishResult, ReviewDb>() {
+ public PublishResult run(ReviewDb db, Transaction txn, boolean retry)
+ throws OrmException {
+ return doPublishComments(psid, message, approvals, db, txn);
+ }
+ });
+
+ try {
+ final CommentSender cm;
+ cm = commentSenderFactory.create(r.change);
+ cm.setFrom(getAccountId());
+ cm.setPatchSet(r.patchSet, patchSetInfoFactory.get(psid));
+ cm.setChangeMessage(r.message);
+ cm.setPatchLineComments(r.comments);
+ cm.setReviewDb(db);
+ cm.send();
+ } catch (EmailException e) {
+ log.error("Cannot send comments by email for patch set " + psid, e);
+ throw new Failure(e);
+ } catch (PatchSetInfoNotAvailableException e) {
+ log.error("Failed to obtain PatchSetInfo for patch set " + psid, e);
+ throw new Failure(e);
+ }
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+ /**
+ * Update the reviewed status for the file by user @code{account}
+ */
+ public void setReviewedByCurrentUser(final Key patchKey,
+ final boolean reviewed, AsyncCallback<VoidResult> callback) {
+ run(callback, new Action<VoidResult>() {
+ public VoidResult run(ReviewDb db) throws OrmException {
+ Account.Id account = getAccountId();
+ AccountPatchReview.Key key =
+ new AccountPatchReview.Key(patchKey, account);
+ AccountPatchReview apr = db.accountPatchReviews().get(key);
+ if (apr == null && reviewed) {
+ db.accountPatchReviews().insert(
+ Collections.singleton(new AccountPatchReview(patchKey, account)));
+ } else if (apr != null && !reviewed) {
+ db.accountPatchReviews().delete(Collections.singleton(apr));
+ }
+ return VoidResult.INSTANCE;
+ }
+ });
+ }
+
+
+ private static class PublishResult {
+ Change change;
+ PatchSet patchSet;
+ ChangeMessage message;
+ List<PatchLineComment> comments;
+ }
+
+ private PublishResult doPublishComments(final PatchSet.Id psid,
+ final String messageText, final Set<ApprovalCategoryValue.Id> approvals,
+ final ReviewDb db, final Transaction txn) throws OrmException {
+ final PublishResult r = new PublishResult();
+ final Account.Id me = getAccountId();
+ r.change = db.changes().get(psid.getParentKey());
+ r.patchSet = db.patchSets().get(psid);
+ if (r.change == null || r.patchSet == null) {
+ throw new OrmException(new NoSuchEntityException());
+ }
+
+ final boolean iscurrent = psid.equals(r.change.currentPatchSetId());
+ r.comments = db.patchComments().draft(psid, me).toList();
+ for (final PatchLineComment c : r.comments) {
+ c.setStatus(PatchLineComment.Status.PUBLISHED);
+ c.updated();
+ }
+ db.patchComments().update(r.comments, txn);
+
+ final StringBuilder msgbuf = new StringBuilder();
+ final Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> values =
+ new HashMap<ApprovalCategory.Id, ApprovalCategoryValue.Id>();
+ for (final ApprovalCategoryValue.Id v : approvals) {
+ values.put(v.getParentKey(), v);
+ }
+
+ final boolean applyApprovals = iscurrent && r.change.getStatus().isOpen();
+ final Map<ApprovalCategory.Id, PatchSetApproval> have =
+ new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ for (PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(psid, me)) {
+ have.put(a.getCategoryId(), a);
+ }
+ for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
+ final ApprovalCategoryValue.Id v = values.get(at.getCategory().getId());
+ if (v == null) {
+ continue;
+ }
+
+ final ApprovalCategoryValue val = at.getValue(v.get());
+ if (val == null) {
+ continue;
+ }
+
+ PatchSetApproval mycatpp = have.remove(v.getParentKey());
+ if (mycatpp == null) {
+ if (msgbuf.length() > 0) {
+ msgbuf.append("; ");
+ }
+ msgbuf.append(val.getName());
+ if (applyApprovals) {
+ mycatpp =
+ new PatchSetApproval(new PatchSetApproval.Key(psid, me, v
+ .getParentKey()), v.get());
+ db.patchSetApprovals().insert(Collections.singleton(mycatpp), txn);
+ }
+
+ } else if (mycatpp.getValue() != v.get()) {
+ if (msgbuf.length() > 0) {
+ msgbuf.append("; ");
+ }
+ msgbuf.append(val.getName());
+ if (applyApprovals) {
+ mycatpp.setValue(v.get());
+ mycatpp.setGranted();
+ db.patchSetApprovals().update(Collections.singleton(mycatpp), txn);
+ }
+ }
+ }
+ if (applyApprovals) {
+ db.patchSetApprovals().delete(have.values(), txn);
+ }
+
+ if (msgbuf.length() > 0) {
+ msgbuf.insert(0, "Patch Set " + psid.get() + ": ");
+ msgbuf.append("\n\n");
+ } else if (!iscurrent) {
+ msgbuf.append("Patch Set " + psid.get() + ":\n\n");
+ }
+ if (messageText != null) {
+ msgbuf.append(messageText);
+ }
+ if (msgbuf.length() > 0) {
+ r.message =
+ new ChangeMessage(new ChangeMessage.Key(r.change.getId(), ChangeUtil
+ .messageUUID(db)), me);
+ r.message.setMessage(msgbuf.toString());
+ db.changeMessages().insert(Collections.singleton(r.message), txn);
+ }
+
+ ChangeUtil.updated(r.change);
+ db.changes().update(Collections.singleton(r.change), txn);
+ return r;
+ }
+
+ public void addReviewers(final Change.Id id, final List<String> reviewers,
+ final AsyncCallback<AddReviewerResult> callback) {
+ addReviewerFactory.create(id, reviewers).to(callback);
+ }
+
+ public void userApprovals(final Set<Change.Id> cids, final Account.Id aid,
+ final AsyncCallback<ApprovalSummarySet> callback) {
+ run(callback, new Action<ApprovalSummarySet>() {
+ public ApprovalSummarySet run(ReviewDb db)
+ throws OrmException {
+ final Map<Change.Id, ApprovalSummary> approvals =
+ new HashMap<Change.Id, ApprovalSummary>();
+ final AccountInfoCacheFactory aicFactory =
+ accountInfoCacheFactory.create();
+
+ aicFactory.want(aid);
+ for (final Change.Id id : cids) {
+ try {
+ final ChangeControl cc = changeControlFactory.validateFor(id);
+ final Change change = cc.getChange();
+ final PatchSet.Id ps_id = change.currentPatchSetId();
+ final Map<ApprovalCategory.Id, PatchSetApproval> psas =
+ new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ final FunctionState fs =
+ functionStateFactory.create(change, ps_id, psas.values());
+
+ for (final PatchSetApproval ca : db.patchSetApprovals()
+ .byPatchSetUser(ps_id, aid)) {
+ final ApprovalCategory.Id category = ca.getCategoryId();
+ if (change.getStatus().isOpen()) {
+ fs.normalize(approvalTypes.getApprovalType(category), ca);
+ }
+ if (ca.getValue() == 0
+ || ApprovalCategory.SUBMIT.equals(category)) {
+ continue;
+ }
+ psas.put(category, ca);
+ }
+
+ approvals.put(id, new ApprovalSummary(psas.values()));
+ } catch (NoSuchChangeException nsce) {
+ /* The user has no access to see this change, so we
+ * simply do not provide any details about it.
+ */
+ }
+ }
+ return new ApprovalSummarySet(aicFactory.create(), approvals);
+ }
+ });
+ }
+
+ public void strongestApprovals(final Set<Change.Id> cids,
+ final AsyncCallback<ApprovalSummarySet> callback) {
+ run(callback, new Action<ApprovalSummarySet>() {
+ public ApprovalSummarySet run(ReviewDb db)
+ throws OrmException {
+ final Map<Change.Id, ApprovalSummary> approvals =
+ new HashMap<Change.Id, ApprovalSummary>();
+ final AccountInfoCacheFactory aicFactory =
+ accountInfoCacheFactory.create();
+
+ for (final Change.Id id : cids) {
+ try {
+ final ChangeControl cc = changeControlFactory.validateFor(id);
+ final Change change = cc.getChange();
+ final PatchSet.Id ps_id = change.currentPatchSetId();
+ final Map<ApprovalCategory.Id, PatchSetApproval> psas =
+ new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ final FunctionState fs =
+ functionStateFactory.create(change, ps_id, psas.values());
+
+ for (PatchSetApproval ca : db.patchSetApprovals()
+ .byPatchSet(ps_id)) {
+ final ApprovalCategory.Id category = ca.getCategoryId();
+ if (change.getStatus().isOpen()) {
+ fs.normalize(approvalTypes.getApprovalType(category), ca);
+ }
+ if (ca.getValue() == 0
+ || ApprovalCategory.SUBMIT.equals(category)) {
+ continue;
+ }
+ boolean keep = true;
+ if (psas.containsKey(category)) {
+ final short oldValue = psas.get(category).getValue();
+ final short newValue = ca.getValue();
+ keep = (Math.abs(oldValue) < Math.abs(newValue))
+ || ((Math.abs(oldValue) == Math.abs(newValue)
+ && (newValue < oldValue)));
+ }
+ if (keep) {
+ aicFactory.want(ca.getAccountId());
+ psas.put(category, ca);
+ }
+ }
+
+ approvals.put(id, new ApprovalSummary(psas.values()));
+ } catch (NoSuchChangeException nsce) {
+ /* The user has no access to see this change, so we
+ * simply do not provide any details about it.
+ */
+ }
+ }
+
+ return new ApprovalSummarySet(aicFactory.create(), approvals);
+ }
+ });
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchModule.java
new file mode 100644
index 0000000000..0b58702561
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchModule.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.gerrit.server.config.FactoryModule;
+
+public class PatchModule extends RpcServletModule {
+ public PatchModule() {
+ super(UiRpcModule.PREFIX);
+ }
+
+ @Override
+ protected void configureServlets() {
+ install(new FactoryModule() {
+ @Override
+ protected void configure() {
+ factory(AddReviewer.Factory.class);
+ factory(CommentDetailFactory.Factory.class);
+ factory(PatchScriptFactory.Factory.class);
+ factory(SaveDraft.Factory.class);
+ }
+ });
+ rpc(PatchDetailServiceImpl.class);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java
new file mode 100644
index 0000000000..673ef0faa4
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptBuilder.java
@@ -0,0 +1,390 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.EditList;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchScriptSettings;
+import com.google.gerrit.common.data.SparseFileContent;
+import com.google.gerrit.common.data.PatchScript.DisplayMethod;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.Patch.PatchType;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.Text;
+
+import eu.medsea.mimeutil.MimeType;
+import eu.medsea.mimeutil.MimeUtil2;
+
+import org.eclipse.jgit.diff.Edit;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+class PatchScriptBuilder {
+ static final int MAX_CONTEXT = 5000000;
+ static final int BIG_FILE = 9000;
+
+ private static final Comparator<Edit> EDIT_SORT = new Comparator<Edit>() {
+ @Override
+ public int compare(final Edit o1, final Edit o2) {
+ return o1.getBeginA() - o2.getBeginA();
+ }
+ };
+
+ private final List<String> header;
+ private Repository db;
+ private Change change;
+ private PatchScriptSettings settings;
+ private ObjectId aId;
+ private ObjectId bId;
+
+ private final Side a;
+ private final Side b;
+
+ private List<Edit> edits;
+ private final FileTypeRegistry registry;
+
+ PatchScriptBuilder(final FileTypeRegistry ftr) {
+ header = new ArrayList<String>();
+ a = new Side();
+ b = new Side();
+ registry = ftr;
+ }
+
+ void setRepository(final Repository r) {
+ db = r;
+ }
+
+ void setChange(final Change c) {
+ this.change = c;
+ }
+
+ void setSettings(final PatchScriptSettings s) {
+ settings = s;
+ }
+
+ void setTrees(final ObjectId a, final ObjectId b) {
+ aId = a;
+ bId = b;
+ }
+
+ private int context() {
+ return settings.getContext();
+ }
+
+ PatchScript toPatchScript(final PatchListEntry contentWS,
+ final CommentDetail comments, final PatchListEntry contentAct)
+ throws IOException {
+ if (contentAct.getPatchType() == PatchType.N_WAY) {
+ // For a diff --cc format we don't support converting it into
+ // a patch script. Instead treat everything as a file header.
+ //
+ return new PatchScript(change.getKey(), contentAct.getHeaderLines(),
+ settings, a.dst, b.dst, Collections.<Edit> emptyList(),
+ a.displayMethod, b.displayMethod);
+ }
+
+ a.path = oldName(contentAct);
+ b.path = newName(contentAct);
+
+ a.resolve(null, aId);
+ b.resolve(a, bId);
+
+ edits = new ArrayList<Edit>(contentAct.getEdits());
+ ensureCommentsVisible(comments);
+ header.addAll(contentAct.getHeaderLines());
+
+ if (a.mode == FileMode.GITLINK || b.mode == FileMode.GITLINK) {
+
+ } else if (a.src == b.src && a.src.size() <= context()
+ && contentAct.getEdits().isEmpty()) {
+ // Odd special case; the files are identical (100% rename or copy)
+ // and the user has asked for context that is larger than the file.
+ // Send them the entire file, with an empty edit after the last line.
+ //
+ for (int i = 0; i < a.src.size(); i++) {
+ a.src.addLineTo(a.dst, i);
+ }
+ edits = new ArrayList<Edit>(1);
+ edits.add(new Edit(a.src.size(), a.src.size()));
+ } else {
+ if (BIG_FILE < Math.max(a.src.size(), b.src.size()) && 25 < context()) {
+ settings.setContext(25);
+ }
+ packContent();
+ }
+
+ if (contentWS != contentAct) {
+ // The edit list we used to pack the file contents doesn't honor the
+ // whitespace settings requested. Instead we must rebuild our edit
+ // list around the whitespace edit list.
+ //
+ edits = new ArrayList<Edit>(contentWS.getEdits());
+ ensureCommentsVisible(comments);
+ }
+
+ return new PatchScript(change.getKey(), header, settings, a.dst, b.dst,
+ edits, a.displayMethod, b.displayMethod);
+ }
+
+ private static String oldName(final PatchListEntry entry) {
+ switch (entry.getChangeType()) {
+ case ADDED:
+ return null;
+ case DELETED:
+ case MODIFIED:
+ return entry.getNewName();
+ case COPIED:
+ case RENAMED:
+ default:
+ return entry.getOldName();
+ }
+ }
+
+ private static String newName(final PatchListEntry entry) {
+ switch (entry.getChangeType()) {
+ case DELETED:
+ return null;
+ case ADDED:
+ case MODIFIED:
+ case COPIED:
+ case RENAMED:
+ default:
+ return entry.getNewName();
+ }
+ }
+
+ private void ensureCommentsVisible(final CommentDetail comments) {
+ if (comments.getCommentsA().isEmpty() && comments.getCommentsB().isEmpty()) {
+ // No comments, no additional dummy edits are required.
+ //
+ return;
+ }
+
+ // Construct empty Edit blocks around each location where a comment is.
+ // This will force the later packContent method to include the regions
+ // containing comments, potentially combining those regions together if
+ // they have overlapping contexts. UI renders will also be able to make
+ // correct hunks from this, but because the Edit is empty they will not
+ // style it specially.
+ //
+ final List<Edit> empty = new ArrayList<Edit>();
+ int lastLine;
+
+ lastLine = -1;
+ for (PatchLineComment plc : comments.getCommentsA()) {
+ final int a = plc.getLine();
+ if (lastLine != a) {
+ final int b = mapA2B(a - 1);
+ if (0 <= b) {
+ safeAdd(empty, new Edit(a - 1, b));
+ }
+ lastLine = a;
+ }
+ }
+
+ lastLine = -1;
+ for (PatchLineComment plc : comments.getCommentsB()) {
+ final int b = plc.getLine();
+ if (lastLine != b) {
+ final int a = mapB2A(b - 1);
+ if (0 <= a) {
+ safeAdd(empty, new Edit(a, b - 1));
+ }
+ lastLine = b;
+ }
+ }
+
+ // Sort the final list by the index in A, so packContent can combine
+ // them correctly later.
+ //
+ edits.addAll(empty);
+ Collections.sort(edits, EDIT_SORT);
+ }
+
+ private void safeAdd(final List<Edit> empty, final Edit toAdd) {
+ final int a = toAdd.getBeginA();
+ final int b = toAdd.getBeginB();
+ for (final Edit e : edits) {
+ if (e.getBeginA() <= a && a <= e.getEndA()) {
+ return;
+ }
+ if (e.getBeginB() <= b && b <= e.getEndB()) {
+ return;
+ }
+ }
+ empty.add(toAdd);
+ }
+
+ private int mapA2B(final int a) {
+ if (edits.isEmpty()) {
+ // Magic special case of an unmodified file.
+ //
+ return a;
+ }
+
+ for (int i = 0; i < edits.size(); i++) {
+ final Edit e = edits.get(i);
+ if (a < e.getBeginA()) {
+ if (i == 0) {
+ // Special case of context at start of file.
+ //
+ return a;
+ }
+ return e.getBeginB() - (e.getBeginA() - a);
+ }
+ if (e.getBeginA() <= a && a <= e.getEndA()) {
+ return -1;
+ }
+ }
+
+ final Edit last = edits.get(edits.size() - 1);
+ return last.getBeginB() + (a - last.getEndA());
+ }
+
+ private int mapB2A(final int b) {
+ if (edits.isEmpty()) {
+ // Magic special case of an unmodified file.
+ //
+ return b;
+ }
+
+ for (int i = 0; i < edits.size(); i++) {
+ final Edit e = edits.get(i);
+ if (b < e.getBeginB()) {
+ if (i == 0) {
+ // Special case of context at start of file.
+ //
+ return b;
+ }
+ return e.getBeginA() - (e.getBeginB() - b);
+ }
+ if (e.getBeginB() <= b && b <= e.getEndB()) {
+ return -1;
+ }
+ }
+
+ final Edit last = edits.get(edits.size() - 1);
+ return last.getBeginA() + (b - last.getEndB());
+ }
+
+ private void packContent() {
+ EditList list = new EditList(edits, context(), a.src.size(), b.src.size());
+ for (final EditList.Hunk hunk : list.getHunks()) {
+ while (hunk.next()) {
+ if (hunk.isContextLine()) {
+ a.src.addLineTo(a.dst, hunk.getCurA());
+ hunk.incBoth();
+
+ } else if (hunk.isDeletedA()) {
+ a.src.addLineTo(a.dst, hunk.getCurA());
+ hunk.incA();
+
+ } else if (hunk.isInsertedB()) {
+ b.src.addLineTo(b.dst, hunk.getCurB());
+ hunk.incB();
+ }
+ }
+ }
+ }
+
+ private class Side {
+ String path;
+ ObjectId id;
+ FileMode mode;
+ Text src;
+ MimeType mimeType = MimeUtil2.UNKNOWN_MIME_TYPE;
+ DisplayMethod displayMethod = DisplayMethod.DIFF;
+ final SparseFileContent dst = new SparseFileContent();
+
+ void resolve(final Side other, final ObjectId within) throws IOException {
+ try {
+ final TreeWalk tw = find(within);
+
+ id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
+ mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
+
+ final boolean reuse =
+ other != null && other.id.equals(id) && other.mode == mode;
+
+ if (reuse) {
+ src = other.src;
+
+ } else if (mode.getObjectType() == Constants.OBJ_BLOB) {
+ final ObjectLoader ldr = db.openObject(id);
+ if (ldr == null) {
+ throw new MissingObjectException(id, Constants.TYPE_BLOB);
+ }
+ final byte[] data = ldr.getCachedBytes();
+ if (ldr.getType() != Constants.OBJ_BLOB) {
+ throw new IncorrectObjectTypeException(id, Constants.TYPE_BLOB);
+ }
+ src = new Text(data);
+
+ } else {
+ src = Text.EMPTY;
+ }
+
+ if (reuse) {
+ mimeType = other.mimeType;
+ displayMethod = other.displayMethod;
+
+ } else if (src.getContent().length > 0 && FileMode.SYMLINK != mode) {
+ mimeType = registry.getMimeType(path, src.getContent());
+ if ("image".equals(mimeType.getMediaType())
+ && registry.isSafeInline(mimeType)) {
+ displayMethod = DisplayMethod.IMG;
+ }
+ }
+
+ if (mode == FileMode.MISSING) {
+ displayMethod = DisplayMethod.NONE;
+ }
+
+ dst.setMissingNewlineAtEnd(src.isMissingNewlineAtEnd());
+ dst.setSize(src.size());
+ } catch (IOException err) {
+ throw new IOException("Cannot read " + within.name() + ":" + path, err);
+ }
+ }
+
+ private TreeWalk find(final ObjectId within) throws MissingObjectException,
+ IncorrectObjectTypeException, CorruptObjectException, IOException {
+ if (path == null) {
+ return null;
+ }
+ final RevWalk rw = new RevWalk(db);
+ final RevTree tree = rw.parseTree(within);
+ return TreeWalk.forPath(db, path, tree);
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java
new file mode 100644
index 0000000000..24b17cac11
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchScriptFactory.java
@@ -0,0 +1,236 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.common.data.CommentDetail;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchScriptSettings;
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGeneralPreferences;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.PatchListKey;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+
+
+class PatchScriptFactory extends Handler<PatchScript> {
+ interface Factory {
+ PatchScriptFactory create(Patch.Key patchKey,
+ @Assisted("patchSetA") PatchSet.Id patchSetA,
+ @Assisted("patchSetB") PatchSet.Id patchSetB,
+ PatchScriptSettings settings);
+ }
+
+ private static final Logger log =
+ LoggerFactory.getLogger(PatchScriptFactory.class);
+
+ private final GitRepositoryManager repoManager;
+ private final FileTypeRegistry registry;
+ private final PatchListCache patchListCache;
+ private final ReviewDb db;
+ private final ChangeControl.Factory changeControlFactory;
+
+ private final Patch.Key patchKey;
+ @Nullable
+ private final PatchSet.Id psa;
+ private final PatchSet.Id psb;
+ private final PatchScriptSettings settings;
+
+ private final PatchSet.Id patchSetId;
+ private final Change.Id changeId;
+
+ private Change change;
+ private PatchSet patchSet;
+ private Project.NameKey projectKey;
+ private Repository git;
+
+ private ChangeControl control;
+
+ private ObjectId aId;
+
+ private ObjectId bId;
+
+ @Inject
+ PatchScriptFactory(final GitRepositoryManager grm, final FileTypeRegistry ftr,
+ final PatchListCache patchListCache, final ReviewDb db,
+ final ChangeControl.Factory changeControlFactory,
+ @Assisted final Patch.Key patchKey,
+ @Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
+ @Assisted("patchSetB") final PatchSet.Id patchSetB,
+ @Assisted final PatchScriptSettings settings) {
+ this.repoManager = grm;
+ this.registry = ftr;
+ this.patchListCache = patchListCache;
+ this.db = db;
+ this.changeControlFactory = changeControlFactory;
+
+ this.patchKey = patchKey;
+ this.psa = patchSetA;
+ this.psb = patchSetB;
+ this.settings = settings;
+
+ patchSetId = patchKey.getParentKey();
+ changeId = patchSetId.getParentKey();
+ }
+
+ @Override
+ public PatchScript call() throws OrmException, NoSuchChangeException {
+ validatePatchSetId(psa);
+ validatePatchSetId(psb);
+
+ control = changeControlFactory.validateFor(changeId);
+ change = control.getChange();
+ patchSet = db.patchSets().get(patchSetId);
+ if (patchSet == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ projectKey = change.getProject();
+ aId = psa != null ? toObjectId(db, psa) : null;
+ bId = toObjectId(db, psb);
+
+ try {
+ git = repoManager.openRepository(projectKey.get());
+ } catch (RepositoryNotFoundException e) {
+ log.error("Repository " + projectKey + " not found", e);
+ throw new NoSuchChangeException(changeId, e);
+ }
+
+ final String fileName = patchKey.getFileName();
+ try {
+ final PatchList list = listFor(keyFor(settings.getWhitespace()));
+ final PatchScriptBuilder b = newBuilder(list);
+ final PatchListEntry contentWS = list.get(fileName);
+ final CommentDetail comments = allComments(db);
+
+ final PatchListEntry contentActual;
+ if (settings.getWhitespace() == Whitespace.IGNORE_NONE) {
+ contentActual = contentWS;
+ } else {
+ // If we are ignoring whitespace in some form, we still need to know
+ // where the post-image differs so we can ensure the post-image lines
+ // are still packed for the client to display.
+ //
+ contentActual = listFor(keyFor(Whitespace.IGNORE_NONE)).get(fileName);
+ }
+
+ try {
+ return b.toPatchScript(contentWS, comments, contentActual);
+ } catch (IOException e) {
+ log.error("File content unavailable", e);
+ throw new NoSuchChangeException(changeId, e);
+ }
+ } finally {
+ git.close();
+ }
+ }
+
+ private PatchListKey keyFor(final Whitespace whitespace) {
+ return new PatchListKey(projectKey, aId, bId, whitespace);
+ }
+
+ private PatchList listFor(final PatchListKey key) {
+ return patchListCache.get(key);
+ }
+
+ private PatchScriptBuilder newBuilder(final PatchList list)
+ throws NoSuchChangeException {
+ final PatchScriptSettings s = new PatchScriptSettings(settings);
+
+ final int ctx = settings.getContext();
+ if (ctx == AccountGeneralPreferences.WHOLE_FILE_CONTEXT)
+ s.setContext(PatchScriptBuilder.MAX_CONTEXT);
+ else if (0 <= ctx && ctx <= PatchScriptBuilder.MAX_CONTEXT)
+ s.setContext(ctx);
+ else
+ throw new NoSuchChangeException(changeId);
+
+ final PatchScriptBuilder b = new PatchScriptBuilder(registry);
+ b.setRepository(git);
+ b.setChange(change);
+ b.setSettings(s);
+ b.setTrees(list.getOldId(), list.getNewId());
+ return b;
+ }
+
+ private ObjectId toObjectId(final ReviewDb db, final PatchSet.Id psId)
+ throws OrmException, NoSuchChangeException {
+ if (!changeId.equals(psId.getParentKey())) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ final PatchSet ps = db.patchSets().get(psId);
+ if (ps == null || ps.getRevision() == null
+ || ps.getRevision().get() == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ try {
+ return ObjectId.fromString(ps.getRevision().get());
+ } catch (IllegalArgumentException e) {
+ log.error("Patch set " + psId + " has invalid revision");
+ throw new NoSuchChangeException(changeId, e);
+ }
+ }
+
+ private void validatePatchSetId(final PatchSet.Id psId)
+ throws NoSuchChangeException {
+ if (psId == null) { // OK, means use base;
+ } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
+ } else {
+ throw new NoSuchChangeException(changeId);
+ }
+ }
+
+ private CommentDetail allComments(final ReviewDb db) throws OrmException {
+ final CommentDetail r = new CommentDetail(psa, psb);
+ final String pn = patchKey.get();
+ for (PatchLineComment p : db.patchComments().published(changeId, pn)) {
+ r.include(p);
+ }
+
+ if (control.getCurrentUser() instanceof IdentifiedUser) {
+ for (PatchLineComment p : db.patchComments().draft(changeId, pn,
+ ((IdentifiedUser) control.getCurrentUser()).getAccountId())) {
+ r.include(p);
+ }
+ }
+ return r;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java
new file mode 100644
index 0000000000..1fbc559416
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/SaveDraft.java
@@ -0,0 +1,103 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.patch;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+
+class SaveDraft extends Handler<PatchLineComment> {
+ interface Factory {
+ SaveDraft create(PatchLineComment comment);
+ }
+
+ private final ChangeControl.Factory changeControlFactory;
+ private final ReviewDb db;
+ private final IdentifiedUser currentUser;
+
+ private final PatchLineComment comment;
+
+ @Inject
+ SaveDraft(final ChangeControl.Factory changeControlFactory,
+ final ReviewDb db, final IdentifiedUser currentUser,
+ @Assisted final PatchLineComment comment) {
+ this.changeControlFactory = changeControlFactory;
+ this.db = db;
+ this.currentUser = currentUser;
+
+ this.comment = comment;
+ }
+
+ @Override
+ public PatchLineComment call() throws NoSuchChangeException, OrmException {
+ if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
+ throw new IllegalStateException("Comment published");
+ }
+
+ final Patch.Key patchKey = comment.getKey().getParentKey();
+ final PatchSet.Id patchSetId = patchKey.getParentKey();
+ final Change.Id changeId = patchKey.getParentKey().getParentKey();
+ changeControlFactory.validateFor(changeId);
+ if (db.patchSets().get(patchSetId) == null) {
+ throw new NoSuchChangeException(changeId);
+ }
+
+ final Account.Id me = currentUser.getAccountId();
+ if (comment.getKey().get() == null) {
+ if (comment.getLine() < 1) {
+ throw new IllegalStateException("Comment line must be >= 1, not "
+ + comment.getLine());
+ }
+
+ if (comment.getParentUuid() != null) {
+ final PatchLineComment parent =
+ db.patchComments().get(
+ new PatchLineComment.Key(patchKey, comment.getParentUuid()));
+ if (parent == null || parent.getSide() != comment.getSide()) {
+ throw new IllegalStateException("Parent comment must be on same side");
+ }
+ }
+
+ final PatchLineComment nc =
+ new PatchLineComment(new PatchLineComment.Key(patchKey, ChangeUtil
+ .messageUUID(db)), comment.getLine(), me, comment.getParentUuid());
+ nc.setSide(comment.getSide());
+ nc.setMessage(comment.getMessage());
+ db.patchComments().insert(Collections.singleton(nc));
+ return nc;
+
+ } else {
+ if (!me.equals(comment.getAuthor())) {
+ throw new NoSuchChangeException(changeId);
+ }
+ comment.updated();
+ db.patchComments().update(Collections.singleton(comment));
+ return comment;
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java
new file mode 100644
index 0000000000..a368706d6e
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddBranch.java
@@ -0,0 +1,185 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.common.errors.InvalidRevisionException;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.ObjectWalk;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+
+class AddBranch extends Handler<List<Branch>> {
+ private static final Logger log = LoggerFactory.getLogger(AddBranch.class);
+
+ interface Factory {
+ AddBranch create(@Assisted Project.NameKey projectName,
+ @Assisted("branchName") String branchName,
+ @Assisted("startingRevision") String startingRevision);
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final ListBranches.Factory listBranchesFactory;
+ private final IdentifiedUser identifiedUser;
+ private final GitRepositoryManager repoManager;
+ private final ReplicationQueue replication;
+
+ private final Project.NameKey projectName;
+ private final String branchName;
+ private final String startingRevision;
+
+ @Inject
+ AddBranch(final ProjectControl.Factory projectControlFactory,
+ final ListBranches.Factory listBranchesFactory,
+ final IdentifiedUser identifiedUser, final GitRepositoryManager repoManager,
+ final ReplicationQueue replication,
+
+ @Assisted Project.NameKey projectName,
+ @Assisted("branchName") String branchName,
+ @Assisted("startingRevision") String startingRevision) {
+ this.projectControlFactory = projectControlFactory;
+ this.listBranchesFactory = listBranchesFactory;
+ this.identifiedUser = identifiedUser;
+ this.repoManager = repoManager;
+ this.replication = replication;
+
+ this.projectName = projectName;
+ this.branchName = branchName;
+ this.startingRevision = startingRevision;
+ }
+
+ @Override
+ public List<Branch> call() throws NoSuchProjectException,
+ InvalidNameException, InvalidRevisionException, IOException {
+ final ProjectControl projectControl =
+ projectControlFactory.validateFor(projectName, ProjectControl.OWNER
+ | ProjectControl.VISIBLE);
+
+ String refname = branchName;
+ while (refname.startsWith("/")) {
+ refname = refname.substring(1);
+ }
+ if (!refname.startsWith(Constants.R_REFS)) {
+ refname = Constants.R_HEADS + refname;
+ }
+ if (!Repository.isValidRefName(refname)) {
+ throw new InvalidNameException();
+ }
+ if (!projectControl.canCreateRef(refname)) {
+ throw new IllegalStateException("Cannot create " + refname);
+ }
+
+ final Branch.NameKey name = new Branch.NameKey(projectName, refname);
+ final Repository repo = repoManager.openRepository(projectName.get());
+ try {
+ final ObjectId revid = parseStartingRevision(repo);
+ final RevWalk rw = verifyConnected(repo, revid);
+
+ try {
+ final RefUpdate u = repo.updateRef(refname);
+ u.setExpectedOldObjectId(ObjectId.zeroId());
+ u.setNewObjectId(revid);
+ u.setRefLogIdent(identifiedUser.newPersonIdent());
+ u.setRefLogMessage("created via web from " + startingRevision, false);
+ final RefUpdate.Result result = u.update(rw);
+ switch (result) {
+ case FAST_FORWARD:
+ case NEW:
+ case NO_CHANGE:
+ replication.scheduleUpdate(name.getParentKey(), refname);
+ break;
+ default: {
+ final String msg =
+ "Cannot create branch " + name + ": " + result.name();
+ log.error(msg);
+ throw new IOException(result.name());
+ }
+ }
+ } catch (IOException err) {
+ log.error("Cannot create branch " + name, err);
+ throw err;
+ }
+ } finally {
+ repo.close();
+ }
+
+ return listBranchesFactory.create(projectName).call();
+ }
+
+ private ObjectId parseStartingRevision(final Repository repo)
+ throws InvalidRevisionException {
+ try {
+ final ObjectId revid = repo.resolve(startingRevision);
+ if (revid == null) {
+ throw new InvalidRevisionException();
+ }
+ return revid;
+ } catch (IOException err) {
+ log.error("Cannot resolve \"" + startingRevision + "\" in project \""
+ + projectName + "\"", err);
+ throw new InvalidRevisionException();
+ }
+ }
+
+ private RevWalk verifyConnected(final Repository repo, final ObjectId revid)
+ throws InvalidRevisionException {
+ try {
+ final ObjectWalk rw = new ObjectWalk(repo);
+ try {
+ rw.markStart(rw.parseCommit(revid));
+ } catch (IncorrectObjectTypeException err) {
+ throw new InvalidRevisionException();
+ }
+ for (final Ref r : repo.getAllRefs().values()) {
+ try {
+ rw.markUninteresting(rw.parseAny(r.getObjectId()));
+ } catch (MissingObjectException err) {
+ continue;
+ }
+ }
+ rw.checkConnectivity();
+ return rw;
+ } catch (IncorrectObjectTypeException err) {
+ throw new InvalidRevisionException();
+ } catch (MissingObjectException err) {
+ throw new InvalidRevisionException();
+ } catch (IOException err) {
+ log.error("Repository \"" + repo.getDirectory()
+ + "\" may be corrupt; suggest running git fsck", err);
+ throw new InvalidRevisionException();
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddProjectRight.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddProjectRight.java
new file mode 100644
index 0000000000..362eb11197
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddProjectRight.java
@@ -0,0 +1,129 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.account.NoSuchGroupException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+
+class AddProjectRight extends Handler<ProjectDetail> {
+ interface Factory {
+ AddProjectRight create(@Assisted Project.NameKey projectName,
+ @Assisted ApprovalCategory.Id categoryId, @Assisted String groupName,
+ @Assisted("min") short min, @Assisted("max") short max);
+ }
+
+ private final ProjectDetailFactory.Factory projectDetailFactory;
+ private final ProjectControl.Factory projectControlFactory;
+ private final ProjectCache projectCache;
+ private final ReviewDb db;
+ private final ApprovalTypes approvalTypes;
+
+ private final Project.NameKey projectName;
+ private final ApprovalCategory.Id categoryId;
+ private final AccountGroup.NameKey groupName;
+ private final short min;
+ private final short max;
+
+ @Inject
+ AddProjectRight(final ProjectDetailFactory.Factory projectDetailFactory,
+ final ProjectControl.Factory projectControlFactory,
+ final ProjectCache projectCache, final ReviewDb db,
+ final ApprovalTypes approvalTypes,
+
+ @Assisted final Project.NameKey projectName,
+ @Assisted final ApprovalCategory.Id categoryId,
+ @Assisted final String groupName, @Assisted("min") final short min,
+ @Assisted("max") final short max) {
+ this.projectDetailFactory = projectDetailFactory;
+ this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
+ this.approvalTypes = approvalTypes;
+ this.db = db;
+
+ this.projectName = projectName;
+ this.categoryId = categoryId;
+ this.groupName = new AccountGroup.NameKey(groupName);
+
+ if (min <= max) {
+ this.min = min;
+ this.max = max;
+ } else {
+ this.min = max;
+ this.max = min;
+ }
+ }
+
+ @Override
+ public ProjectDetail call() throws NoSuchProjectException, OrmException,
+ NoSuchGroupException {
+ final ProjectControl projectControl =
+ projectControlFactory.ownerFor(projectName);
+
+ if (projectControl.getProjectState().isSpecialWildProject()
+ && ApprovalCategory.OWN.equals(categoryId)) {
+ // Giving out control of the WILD_PROJECT to other groups beyond
+ // Administrators is dangerous. Having control over WILD_PROJECT
+ // is about the same as having Administrator access as users are
+ // able to affect grants in all projects on the system.
+ //
+ throw new IllegalArgumentException("Cannot give " + categoryId.get()
+ + " on " + projectName + " " + groupName);
+ }
+
+ final ApprovalType at = approvalTypes.getApprovalType(categoryId);
+ if (at == null || at.getValue(min) == null || at.getValue(max) == null) {
+ throw new IllegalArgumentException("Invalid category " + categoryId
+ + " or range " + min + ".." + max);
+ }
+
+ final AccountGroup group = db.accountGroups().get(groupName);
+ if (group == null) {
+ throw new NoSuchGroupException(groupName);
+ }
+
+ final ProjectRight.Key key =
+ new ProjectRight.Key(projectName, categoryId, group.getId());
+ ProjectRight pr = db.projectRights().get(key);
+ if (pr == null) {
+ pr = new ProjectRight(key);
+ pr.setMinValue(min);
+ pr.setMaxValue(max);
+ db.projectRights().insert(Collections.singleton(pr));
+ } else {
+ pr.setMinValue(min);
+ pr.setMaxValue(max);
+ db.projectRights().update(Collections.singleton(pr));
+ }
+
+ projectCache.evict(projectControl.getProject());
+ return projectDetailFactory.create(projectName).call();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
new file mode 100644
index 0000000000..8831e9071d
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+
+class ChangeProjectSettings extends Handler<ProjectDetail> {
+ interface Factory {
+ ChangeProjectSettings create(@Assisted Project update);
+ }
+
+ private final ProjectDetailFactory.Factory projectDetailFactory;
+ private final ProjectControl.Factory projectControlFactory;
+ private final ProjectCache projectCache;
+ private final ReviewDb db;
+ private final GitRepositoryManager repoManager;
+
+ private final Project update;
+
+ @Inject
+ ChangeProjectSettings(
+ final ProjectDetailFactory.Factory projectDetailFactory,
+ final ProjectControl.Factory projectControlFactory,
+ final ProjectCache projectCache, final ReviewDb db,
+ final GitRepositoryManager grm,
+ @Assisted final Project update) {
+ this.projectDetailFactory = projectDetailFactory;
+ this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
+ this.db = db;
+ this.repoManager = grm;
+
+ this.update = update;
+ }
+
+ @Override
+ public ProjectDetail call() throws NoSuchProjectException, OrmException {
+ final Project.NameKey projectName = update.getNameKey();
+ final ProjectControl projectControl =
+ projectControlFactory.ownerFor(projectName);
+
+ final Project proj = db.projects().get(projectName);
+ if (proj == null) {
+ throw new NoSuchProjectException(projectName);
+ }
+
+ proj.copySettingsFrom(update);
+ db.projects().update(Collections.singleton(proj));
+ projectCache.evict(proj);
+
+ if (!projectControl.getProjectState().isSpecialWildProject()) {
+ repoManager.setProjectDescription(projectName.get(), update.getDescription());
+ }
+
+ return projectDetailFactory.create(projectName).call();
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java
new file mode 100644
index 0000000000..526afe528c
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteBranches.java
@@ -0,0 +1,121 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+class DeleteBranches extends Handler<Set<Branch.NameKey>> {
+ private static final Logger log =
+ LoggerFactory.getLogger(DeleteBranches.class);
+
+ interface Factory {
+ DeleteBranches create(@Assisted Project.NameKey name,
+ @Assisted Set<Branch.NameKey> toRemove);
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final GitRepositoryManager repoManager;
+ private final ReplicationQueue replication;
+
+ private final Project.NameKey projectName;
+ private final Set<Branch.NameKey> toRemove;
+
+ @Inject
+ DeleteBranches(final ProjectControl.Factory projectControlFactory,
+ final GitRepositoryManager repoManager,
+ final ReplicationQueue replication,
+
+ @Assisted Project.NameKey name, @Assisted Set<Branch.NameKey> toRemove) {
+ this.projectControlFactory = projectControlFactory;
+ this.repoManager = repoManager;
+ this.replication = replication;
+
+ this.projectName = name;
+ this.toRemove = toRemove;
+ }
+
+ @Override
+ public Set<Branch.NameKey> call() throws NoSuchProjectException,
+ RepositoryNotFoundException {
+ final ProjectControl projectControl =
+ projectControlFactory.validateFor(projectName, ProjectControl.OWNER
+ | ProjectControl.VISIBLE);
+
+ for (Branch.NameKey k : toRemove) {
+ if (!projectName.equals(k.getParentKey())) {
+ throw new IllegalArgumentException("All keys must be from same project");
+ }
+ if (!projectControl.canDeleteRef(k.get())) {
+ throw new IllegalStateException("Cannot delete " + k.getShortName());
+ }
+ }
+
+ final Set<Branch.NameKey> deleted = new HashSet<Branch.NameKey>();
+ final Repository r = repoManager.openRepository(projectName.get());
+ try {
+ for (final Branch.NameKey branchKey : toRemove) {
+ final String refname = branchKey.get();
+ final RefUpdate.Result result;
+ try {
+ final RefUpdate u = r.updateRef(refname);
+ u.setForceUpdate(true);
+ result = u.delete();
+ } catch (IOException e) {
+ log.error("Cannot delete " + branchKey, e);
+ continue;
+ }
+
+ switch (result) {
+ case NEW:
+ case NO_CHANGE:
+ case FAST_FORWARD:
+ case FORCED:
+ deleted.add(branchKey);
+ replication.scheduleUpdate(projectName, refname);
+ break;
+
+ case REJECTED_CURRENT_BRANCH:
+ log.warn("Cannot delete " + branchKey + ": " + result.name());
+ break;
+
+ default:
+ log.error("Cannot delete " + branchKey + ": " + result.name());
+ break;
+ }
+ }
+ } finally {
+ r.close();
+ }
+ return deleted;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteProjectRights.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteProjectRights.java
new file mode 100644
index 0000000000..d476d8926e
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteProjectRights.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+import java.util.Set;
+
+class DeleteProjectRights extends Handler<VoidResult> {
+ interface Factory {
+ DeleteProjectRights create(@Assisted Project.NameKey projectName,
+ @Assisted Set<ProjectRight.Key> toRemove);
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final ProjectCache projectCache;
+ private final ReviewDb db;
+
+ private final Project.NameKey projectName;
+ private final Set<ProjectRight.Key> toRemove;
+
+ @Inject
+ DeleteProjectRights(final ProjectControl.Factory projectControlFactory,
+ final ProjectCache projectCache, final ReviewDb db,
+
+ @Assisted final Project.NameKey projectName,
+ @Assisted final Set<ProjectRight.Key> toRemove) {
+ this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
+ this.db = db;
+
+ this.projectName = projectName;
+ this.toRemove = toRemove;
+ }
+
+ @Override
+ public VoidResult call() throws NoSuchProjectException, OrmException {
+ final ProjectControl projectControl =
+ projectControlFactory.ownerFor(projectName);
+
+ for (final ProjectRight.Key k : toRemove) {
+ if (!projectName.equals(k.getProjectNameKey())) {
+ throw new IllegalArgumentException("All keys must be from same project");
+ }
+ }
+
+ for (final ProjectRight.Key k : toRemove) {
+ final ProjectRight m = db.projectRights().get(k);
+ if (m != null) {
+ db.projectRights().delete(Collections.singleton(m));
+ }
+ }
+ projectCache.evict(projectControl.getProject());
+ return VoidResult.INSTANCE;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListBranches.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListBranches.java
new file mode 100644
index 0000000000..c8c4409cb4
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ListBranches.java
@@ -0,0 +1,108 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+class ListBranches extends Handler<List<Branch>> {
+ interface Factory {
+ ListBranches create(@Assisted Project.NameKey name);
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final GitRepositoryManager repoManager;
+
+ private final Project.NameKey projectName;
+
+ @Inject
+ ListBranches(final ProjectControl.Factory projectControlFactory,
+ final GitRepositoryManager repoManager,
+
+ @Assisted final Project.NameKey name) {
+ this.projectControlFactory = projectControlFactory;
+ this.repoManager = repoManager;
+
+ this.projectName = name;
+ }
+
+ @Override
+ public List<Branch> call() throws NoSuchProjectException,
+ RepositoryNotFoundException {
+ final ProjectControl projectControl =
+ projectControlFactory.validateFor(projectName, ProjectControl.OWNER
+ | ProjectControl.VISIBLE);
+
+ final List<Branch> branches = new ArrayList<Branch>();
+ final Repository db = repoManager.openRepository(projectName.get());
+ try {
+ final Map<String, Ref> all = db.getAllRefs();
+
+ if (!all.containsKey(Constants.HEAD)) {
+ // The branch pointed to by HEAD doesn't exist yet. Fake
+ // that it exists by returning a Ref with no ObjectId.
+ //
+ try {
+ final String head = db.getFullBranch();
+ if (head != null && head.startsWith(Constants.R_REFS)) {
+ all.put(Constants.HEAD, new Ref(Ref.Storage.LOOSE, Constants.HEAD,
+ head, null));
+ }
+ } catch (IOException e) {
+ // Ignore the failure reading HEAD.
+ }
+ }
+
+ for (final Ref ref : all.values()) {
+ final String name = ref.getName();
+ if (name.startsWith(Constants.R_HEADS)) {
+ final Branch b = new Branch(new Branch.NameKey(projectName, name));
+ if (ref.getObjectId() != null) {
+ b.setRevision(new RevId(ref.getObjectId().name()));
+ }
+ branches.add(b);
+ }
+ }
+ } finally {
+ db.close();
+ }
+ Collections.sort(branches, new Comparator<Branch>() {
+ @Override
+ public int compare(final Branch a, final Branch b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ return branches;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/OwnedProjects.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/OwnedProjects.java
new file mode 100644
index 0000000000..4e7267f00f
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/OwnedProjects.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+class OwnedProjects extends Handler<List<Project>> {
+ interface Factory {
+ OwnedProjects create();
+ }
+
+ private final ProjectControl.Factory projectControlFactory;
+ private final IdentifiedUser user;
+ private final ReviewDb db;
+
+ @Inject
+ OwnedProjects(final ProjectControl.Factory projectControlFactory,
+ final IdentifiedUser user, final ReviewDb db) {
+ this.projectControlFactory = projectControlFactory;
+ this.user = user;
+ this.db = db;
+ }
+
+ @Override
+ public List<Project> call() throws OrmException {
+ final List<Project> result;
+ if (user.isAdministrator()) {
+ result = db.projects().all().toList();
+
+ } else {
+ final HashSet<Project.NameKey> seen = new HashSet<Project.NameKey>();
+ result = new ArrayList<Project>();
+ for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
+ for (final ProjectRight r : db.projectRights().byCategoryGroup(
+ ApprovalCategory.OWN, groupId)) {
+ final Project.NameKey name = r.getProjectNameKey();
+ if (!seen.add(name)) {
+ continue;
+ }
+ try {
+ ProjectControl c = projectControlFactory.ownerFor(name);
+ result.add(c.getProject());
+ } catch (NoSuchProjectException e) {
+ continue;
+ }
+ }
+ }
+ }
+ Collections.sort(result, new Comparator<Project>() {
+ public int compare(final Project a, final Project b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+ return result;
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
new file mode 100644
index 0000000000..7a9ba03a33
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -0,0 +1,104 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.common.data.ProjectAdminService;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwtjsonrpc.client.VoidResult;
+import com.google.inject.Inject;
+
+import java.util.List;
+import java.util.Set;
+
+class ProjectAdminServiceImpl implements ProjectAdminService {
+ private final AddBranch.Factory addBranchFactory;
+ private final AddProjectRight.Factory addProjectRightFactory;
+ private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
+ private final DeleteBranches.Factory deleteBranchesFactory;
+ private final DeleteProjectRights.Factory deleteProjectRightsFactory;
+ private final ListBranches.Factory listBranchesFactory;
+ private final OwnedProjects.Factory ownedProjectsFactory;
+ private final ProjectDetailFactory.Factory projectDetailFactory;
+
+ @Inject
+ ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
+ final AddProjectRight.Factory addProjectRightFactory,
+ final ChangeProjectSettings.Factory changeProjectSettingsFactory,
+ final DeleteBranches.Factory deleteBranchesFactory,
+ final DeleteProjectRights.Factory deleteProjectRightFactory,
+ final ListBranches.Factory listBranchesFactory,
+ final OwnedProjects.Factory ownedProjectsFactory,
+ final ProjectDetailFactory.Factory projectDetailFactory) {
+ this.addBranchFactory = addBranchFactory;
+ this.addProjectRightFactory = addProjectRightFactory;
+ this.changeProjectSettingsFactory = changeProjectSettingsFactory;
+ this.deleteBranchesFactory = deleteBranchesFactory;
+ this.deleteProjectRightsFactory = deleteProjectRightFactory;
+ this.listBranchesFactory = listBranchesFactory;
+ this.ownedProjectsFactory = ownedProjectsFactory;
+ this.projectDetailFactory = projectDetailFactory;
+ }
+
+ public void ownedProjects(final AsyncCallback<List<Project>> callback) {
+ ownedProjectsFactory.create().to(callback);
+ }
+
+ public void projectDetail(final Project.NameKey projectName,
+ final AsyncCallback<ProjectDetail> callback) {
+ projectDetailFactory.create(projectName).to(callback);
+ }
+
+ public void changeProjectSettings(final Project update,
+ final AsyncCallback<ProjectDetail> callback) {
+ changeProjectSettingsFactory.create(update).to(callback);
+ }
+
+ public void deleteRight(final Project.NameKey projectName,
+ final Set<ProjectRight.Key> toRemove,
+ final AsyncCallback<VoidResult> callback) {
+ deleteProjectRightsFactory.create(projectName, toRemove).to(callback);
+ }
+
+ public void addRight(final Project.NameKey projectName,
+ final ApprovalCategory.Id categoryId, final String groupName,
+ final short min, final short max,
+ final AsyncCallback<ProjectDetail> callback) {
+ addProjectRightFactory.create(projectName, categoryId, groupName, min, max)
+ .to(callback);
+ }
+
+ public void listBranches(final Project.NameKey projectName,
+ final AsyncCallback<List<Branch>> callback) {
+ listBranchesFactory.create(projectName).to(callback);
+ }
+
+ public void deleteBranch(final Project.NameKey projectName,
+ final Set<Branch.NameKey> toRemove,
+ final AsyncCallback<Set<Branch.NameKey>> callback) {
+ deleteBranchesFactory.create(projectName, toRemove).to(callback);
+ }
+
+ public void addBranch(final Project.NameKey projectName,
+ final String branchName, final String startingRevision,
+ final AsyncCallback<List<Branch>> callback) {
+ addBranchFactory.create(projectName, branchName, startingRevision).to(
+ callback);
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
new file mode 100644
index 0000000000..7106f32283
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
@@ -0,0 +1,125 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.data.ProjectDetail;
+import com.google.gerrit.httpd.rpc.Handler;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+class ProjectDetailFactory extends Handler<ProjectDetail> {
+ interface Factory {
+ ProjectDetailFactory create(@Assisted Project.NameKey name);
+ }
+
+ private final ApprovalTypes approvalTypes;
+ private final GroupCache groupCache;
+ private final ProjectControl.Factory projectControlFactory;
+
+ private final Project.NameKey projectName;
+ private Map<AccountGroup.Id, AccountGroup> groups;
+
+ @Inject
+ ProjectDetailFactory(final ApprovalTypes approvalTypes,
+ final GroupCache groupCache,
+ final ProjectControl.Factory projectControlFactory,
+
+ @Assisted final Project.NameKey name) {
+ this.approvalTypes = approvalTypes;
+ this.groupCache = groupCache;
+ this.projectControlFactory = projectControlFactory;
+
+ this.projectName = name;
+ }
+
+ @Override
+ public ProjectDetail call() throws NoSuchProjectException {
+ final ProjectState projectState =
+ projectControlFactory.validateFor(projectName,
+ ProjectControl.OWNER | ProjectControl.VISIBLE).getProjectState();
+
+ final ProjectDetail detail = new ProjectDetail();
+ detail.setProject(projectState.getProject());
+
+ groups = new HashMap<AccountGroup.Id, AccountGroup>();
+ final List<ProjectRight> rights = new ArrayList<ProjectRight>();
+ for (final ProjectRight p : projectState.getLocalRights()) {
+ rights.add(p);
+ wantGroup(p.getAccountGroupId());
+ }
+ for (final ProjectRight p : projectState.getInheritedRights()) {
+ rights.add(p);
+ wantGroup(p.getAccountGroupId());
+ }
+ loadGroups();
+
+ Collections.sort(rights, new Comparator<ProjectRight>() {
+ @Override
+ public int compare(final ProjectRight a, final ProjectRight b) {
+ int rc = categoryOf(a).compareTo(categoryOf(b));
+ if (rc == 0) {
+ rc = groupOf(a).compareTo(groupOf(b));
+ }
+ return rc;
+ }
+
+ private String categoryOf(final ProjectRight r) {
+ final ApprovalType type =
+ approvalTypes.getApprovalType(r.getApprovalCategoryId());
+ if (type == null) {
+ return r.getApprovalCategoryId().get();
+ }
+ return type.getCategory().getName();
+ }
+
+ private String groupOf(final ProjectRight r) {
+ return groups.get(r.getAccountGroupId()).getName();
+ }
+ });
+
+ detail.setRights(rights);
+ detail.setGroups(groups);
+ return detail;
+ }
+
+ private void wantGroup(final AccountGroup.Id id) {
+ groups.put(id, null);
+ }
+
+ private void loadGroups() {
+ final Set<AccountGroup.Id> toGet = groups.keySet();
+ groups = new HashMap<AccountGroup.Id, AccountGroup>();
+ for (AccountGroup.Id groupId : toGet) {
+ groups.put(groupId, groupCache.get(groupId));
+ }
+ }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
new file mode 100644
index 0000000000..34de2fd666
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd.rpc.project;
+
+import com.google.gerrit.httpd.rpc.RpcServletModule;
+import com.google.gerrit.httpd.rpc.UiRpcModule;
+import com.google.gerrit.server.config.FactoryModule;
+
+public class ProjectModule extends RpcServletModule {
+ public ProjectModule() {
+ super(UiRpcModule.PREFIX);
+ }
+
+ @Override
+ protected void configureServlets() {
+ install(new FactoryModule() {
+ @Override
+ protected void configure() {
+ factory(AddBranch.Factory.class);
+ factory(AddProjectRight.Factory.class);
+ factory(ChangeProjectSettings.Factory.class);
+ factory(DeleteBranches.Factory.class);
+ factory(DeleteProjectRights.Factory.class);
+ factory(ListBranches.Factory.class);
+ factory(OwnedProjects.Factory.class);
+ factory(ProjectDetailFactory.Factory.class);
+ }
+ });
+ rpc(ProjectAdminServiceImpl.class);
+ }
+}
diff --git a/gerrit-main/.gitignore b/gerrit-main/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-main/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-main/.settings/org.eclipse.core.resources.prefs b/gerrit-main/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-main/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-main/.settings/org.eclipse.core.runtime.prefs b/gerrit-main/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-main/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-main/.settings/org.eclipse.jdt.core.prefs b/gerrit-main/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-main/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-main/.settings/org.eclipse.jdt.ui.prefs b/gerrit-main/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-main/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-main/pom.xml b/gerrit-main/pom.xml
new file mode 100644
index 0000000000..9707789561
--- /dev/null
+++ b/gerrit-main/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-main</artifactId>
+ <name>Gerrit Code Review - Main</name>
+
+ <description>
+ Main class to bootstrap out of a WAR
+ </description>
+</project>
diff --git a/gerrit-main/src/main/java/Main.java b/gerrit-main/src/main/java/Main.java
new file mode 100644
index 0000000000..087a8e3a64
--- /dev/null
+++ b/gerrit-main/src/main/java/Main.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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.
+
+
+public final class Main {
+ // We don't do any real work here because we need to import
+ // the archive lookup code and we cannot import a class in
+ // the default package. So this is just a tiny springboard
+ // to jump into the real main code.
+ //
+
+ public static void main(final String argv[]) throws Exception {
+ com.google.gerrit.main.GerritLauncher.main(argv);
+ }
+
+ private Main() {
+ }
+}
diff --git a/gerrit-main/src/main/java/com/google/gerrit/main/GerritLauncher.java b/gerrit-main/src/main/java/com/google/gerrit/main/GerritLauncher.java
new file mode 100644
index 0000000000..870b185e30
--- /dev/null
+++ b/gerrit-main/src/main/java/com/google/gerrit/main/GerritLauncher.java
@@ -0,0 +1,441 @@
+package com.google.gerrit.main;
+
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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.
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/** Main class for a JAR file to run code from "WEB-INF/lib". */
+public final class GerritLauncher {
+ private static final String pkg = "com.google.gerrit.pgm";
+ private static final String NOT_ARCHIVED = "NOT_ARCHIVED";
+
+ public static void main(final String argv[]) throws Exception {
+ if (argv.length == 0) {
+ File me;
+ try {
+ me = getDistributionArchive();
+ } catch (FileNotFoundException e) {
+ me = null;
+ }
+
+ String jar = me != null ? me.getName() : "gerrit.war";
+ System.err.println("Gerrit Code Review " + getVersion(me));
+ System.err.println("usage: java -jar " + jar + " command [ARG ...]");
+ System.err.println();
+ System.err.println("The most commonly used commands are:");
+ System.err.println(" daemon Run the internal SSH daemon");
+ System.err.println(" version Display the build version number");
+ System.err.println();
+ System.err.println(" ls List files available for cat");
+ System.err.println(" cat FILE Display a file from the archive");
+ System.err.println();
+ System.exit(1);
+ }
+
+ if ("-v".equals(argv[0]) || "--version".equals(argv[0])) {
+ // Special case, jump into the "Version" command which is
+ // compiled somewhere in our library packages.
+ //
+ argv[0] = "Version";
+ }
+
+ final String cmd = argv[0];
+ if ("cat".equals(cmd) || "-p".equals(cmd) || "--cat".equals(cmd)) {
+ // Copy the contents of a file to System.out
+ //
+ if (argv.length == 2) {
+ cat(argv[1]);
+ } else {
+ System.err.println("usage: cat FILE");
+ System.exit(1);
+ }
+
+ } else if ("ls".equals(cmd) || "-l".equals(cmd) || "--ls".equals(cmd)) {
+ // List the available files under WEB-INF/
+ //
+ if (argv.length == 1) {
+ ls();
+ } else {
+ System.err.println("usage: ls");
+ System.exit(1);
+ }
+
+ } else {
+ // Run an arbitrary application class
+ //
+ final ClassLoader cl = libClassLoader();
+ Thread.currentThread().setContextClassLoader(cl);
+ runMain(cl, argv);
+ }
+ }
+
+ private static String getVersion(final File me) {
+ if (me == null) {
+ return "";
+ }
+
+ try {
+ final JarFile jar = new JarFile(me);
+ try {
+ Manifest mf = jar.getManifest();
+ Attributes att = mf.getMainAttributes();
+ String val = att.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+ return val != null ? val : "";
+ } finally {
+ jar.close();
+ }
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ private static void cat(String fileName) throws IOException {
+ while (fileName.startsWith("/")) {
+ fileName = fileName.substring(1);
+ }
+
+ String name;
+ if (fileName.equals("LICENSES.txt")) {
+ name = fileName;
+ } else {
+ name = "WEB-INF/" + fileName;
+ }
+
+ final InputStream in = GerritLauncher.class.getResourceAsStream(name);
+ if (in == null) {
+ System.err.println("error: no such file " + fileName);
+ System.exit(1);
+ }
+
+ try {
+ try {
+ final byte[] buf = new byte[4096];
+ int n;
+ while ((n = in.read(buf)) >= 0) {
+ System.out.write(buf, 0, n);
+ }
+ } finally {
+ System.out.flush();
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ private static void ls() throws IOException {
+ final ZipFile zf = new ZipFile(getDistributionArchive());
+ try {
+ final Enumeration<? extends ZipEntry> e = zf.entries();
+ while (e.hasMoreElements()) {
+ final ZipEntry ze = e.nextElement();
+ String name = ze.getName();
+ boolean show = false;
+ show |= name.startsWith("WEB-INF/");
+ show |= name.equals("LICENSES.txt");
+
+ show &= !ze.isDirectory();
+ show &= !name.startsWith("WEB-INF/classes/");
+ show &= !name.startsWith("WEB-INF/lib/");
+ show &= !name.equals("WEB-INF/web.xml");
+ if (show) {
+ if (name.startsWith("WEB-INF/")) {
+ name = name.substring("WEB-INF/".length());
+ }
+ System.out.println(name);
+ }
+ }
+ } finally {
+ zf.close();
+ }
+ }
+
+ private static void runMain(final ClassLoader loader, final String[] origArgv)
+ throws Exception {
+ String name = origArgv[0];
+ final String[] argv = new String[origArgv.length - 1];
+ System.arraycopy(origArgv, 1, argv, 0, argv.length);
+
+ Class<?> clazz;
+ try {
+ try {
+ clazz = Class.forName(pkg + "." + name, true, loader);
+ } catch (ClassNotFoundException cnfe) {
+ if (name.equals(name.toLowerCase())) {
+ String first = name.substring(0, 1).toUpperCase();
+ String cn = first + name.substring(1);
+ clazz = Class.forName(pkg + "." + cn, true, loader);
+ } else {
+ throw cnfe;
+ }
+ }
+ } catch (ClassNotFoundException cnfe) {
+ System.err.println("fatal: unknown command " + name);
+ System.err.println(" (no " + pkg + "." + name + ")");
+ System.exit(1);
+ return;
+ }
+
+ final Method main;
+ try {
+ main = clazz.getMethod("main", argv.getClass());
+ } catch (SecurityException e) {
+ System.err.println("fatal: unknown command " + name);
+ System.exit(1);
+ return;
+ } catch (NoSuchMethodException e) {
+ System.err.println("fatal: unknown command " + name);
+ System.exit(1);
+ return;
+ }
+
+ final Object res;
+ if ((main.getModifiers() & Modifier.STATIC) == Modifier.STATIC) {
+ res = main.invoke(null, new Object[] {argv});
+ } else {
+ res = main.invoke(clazz.newInstance(), new Object[] {argv});
+ }
+ if (res instanceof Number) {
+ System.exit(((Number) res).intValue());
+ } else {
+ System.exit(0);
+ }
+ }
+
+ private static ClassLoader libClassLoader() throws IOException {
+ final File path;
+ try {
+ path = getDistributionArchive();
+ } catch (FileNotFoundException e) {
+ if (NOT_ARCHIVED == e.getMessage()) {
+ // Assume the CLASSPATH was made complete by the calling process,
+ // as we are likely being run from within a developer's IDE.
+ //
+ return GerritLauncher.class.getClassLoader();
+ }
+ throw e;
+ }
+
+ final ArrayList<URL> jars = new ArrayList<URL>();
+ try {
+ final ZipFile zf = new ZipFile(path);
+ try {
+ final Enumeration<? extends ZipEntry> e = zf.entries();
+ while (e.hasMoreElements()) {
+ final ZipEntry ze = e.nextElement();
+ if (ze.isDirectory()) {
+ continue;
+ }
+
+ if (ze.getName().startsWith("WEB-INF/lib/")) {
+ final File tmp = createTempFile(safeName(ze), ".jar");
+ final FileOutputStream out = new FileOutputStream(tmp);
+ try {
+ final InputStream in = zf.getInputStream(ze);
+ try {
+ final byte[] buf = new byte[4096];
+ int n;
+ while ((n = in.read(buf, 0, buf.length)) > 0) {
+ out.write(buf, 0, n);
+ }
+ } finally {
+ in.close();
+ }
+ } finally {
+ out.close();
+ }
+ jars.add(tmp.toURI().toURL());
+ }
+ }
+ } finally {
+ zf.close();
+ }
+ } catch (IOException e) {
+ throw new IOException("Cannot obtain libraries from " + path, e);
+ }
+
+ if (jars.isEmpty()) {
+ return GerritLauncher.class.getClassLoader();
+ }
+ return new URLClassLoader(jars.toArray(new URL[jars.size()]));
+ }
+
+ private static String safeName(final ZipEntry ze) {
+ // Try to derive the name of the temporary file so it
+ // doesn't completely suck. Best if we can make it
+ // match the name it was in the archive.
+ //
+ String name = ze.getName();
+ if (0 <= name.lastIndexOf('/')) {
+ name = name.substring(name.lastIndexOf('/') + 1);
+ }
+ if (0 <= name.lastIndexOf('.')) {
+ name = name.substring(0, name.lastIndexOf('.'));
+ }
+ if (name.isEmpty()) {
+ name = "code";
+ }
+ return name;
+ }
+
+ private static File myArchive;
+
+ /**
+ * Locate the JAR/WAR file we were launched from.
+ *
+ * @return local path of the Gerrit WAR file.
+ * @throws FileNotFoundException if the code cannot guess the location.
+ */
+ public static File getDistributionArchive() throws FileNotFoundException {
+ if (myArchive == null) {
+ myArchive = locateMyArchive();
+ }
+ return myArchive;
+ }
+
+ private static File locateMyArchive() throws FileNotFoundException {
+ final ClassLoader myCL = GerritLauncher.class.getClassLoader();
+ final String myName =
+ GerritLauncher.class.getName().replace('.', '/') + ".class";
+
+ final URL myClazz = myCL.getResource(myName);
+ if (myClazz == null) {
+ throw new FileNotFoundException("Cannot find JAR: no " + myName);
+ }
+
+ // ZipFile may have the path of our JAR hiding within itself.
+ //
+ try {
+ Field nameField = ZipFile.class.getDeclaredField("name");
+ nameField.setAccessible(true);
+
+ JarFile jar = ((JarURLConnection) myClazz.openConnection()).getJarFile();
+ File path = new File((String) nameField.get(jar));
+ if (path.isFile()) {
+ return path;
+ }
+ } catch (Exception e) {
+ // Nope, that didn't work. Try a different method.
+ //
+ }
+
+ // Maybe this is a local class file, running under a debugger?
+ //
+ if ("file".equals(myClazz.getProtocol())) {
+ final File path = new File(myClazz.getPath());
+ if (path.isFile() && path.getParentFile().isDirectory()) {
+ throw new FileNotFoundException(NOT_ARCHIVED);
+ }
+ }
+
+ // The CodeSource might be able to give us the source as a stream.
+ // If so, copy it to a local file so we have random access to it.
+ //
+ final CodeSource src =
+ GerritLauncher.class.getProtectionDomain().getCodeSource();
+ if (src != null) {
+ try {
+ final InputStream in = src.getLocation().openStream();
+ try {
+ final File tmp = createTempFile("gerrit_", ".zip");
+ final FileOutputStream out = new FileOutputStream(tmp);
+ try {
+ final byte[] buf = new byte[4096];
+ int n;
+ while ((n = in.read(buf, 0, buf.length)) > 0) {
+ out.write(buf, 0, n);
+ }
+ } finally {
+ out.close();
+ }
+ return tmp;
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ // Nope, that didn't work.
+ //
+ }
+ }
+
+ throw new FileNotFoundException("Cannot find local copy of JAR");
+ }
+
+ private static boolean temporaryDirectoryFound;
+ private static File temporaryDirectory;
+
+ private static File createTempFile(String prefix, String suffix)
+ throws IOException {
+ if (!temporaryDirectoryFound) {
+ final File d = File.createTempFile("gerrit_", "_app");
+ if (d.delete() && d.mkdir()) {
+ // Try to lock the directory down to be accessible by us.
+ // We first have to remove all permissions, then add back
+ // only the owner permissions.
+ //
+ d.setWritable(false, false /* all */);
+ d.setReadable(false, false /* all */);
+ d.setExecutable(false, false /* all */);
+
+ d.setWritable(true, true /* owner only */);
+ d.setReadable(true, true /* owner only */);
+ d.setExecutable(true, true /* owner only */);
+
+ d.deleteOnExit();
+ temporaryDirectory = d;
+ }
+ temporaryDirectoryFound = true;
+ }
+
+ if (temporaryDirectory != null) {
+ // If we have a private directory and this name has not yet
+ // been used within the private directory, create it as-is.
+ //
+ final File tmp = new File(temporaryDirectory, prefix + suffix);
+ if (tmp.createNewFile()) {
+ tmp.deleteOnExit();
+ return tmp;
+ }
+ }
+
+ if (!prefix.endsWith("_")) {
+ prefix += "_";
+ }
+
+ final File tmp = File.createTempFile(prefix, suffix, temporaryDirectory);
+ tmp.deleteOnExit();
+ return tmp;
+ }
+
+ private GerritLauncher() {
+ }
+}
diff --git a/gerrit-patch-commonsnet/.gitignore b/gerrit-patch-commonsnet/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-patch-commonsnet/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs b/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-patch-commonsnet/.settings/org.eclipse.core.runtime.prefs b/gerrit-patch-commonsnet/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-patch-commonsnet/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.core.prefs b/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.ui.prefs b/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-patch-commonsnet/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-patch-commonsnet/pom.xml b/gerrit-patch-commonsnet/pom.xml
new file mode 100644
index 0000000000..c9e08aeee3
--- /dev/null
+++ b/gerrit-patch-commonsnet/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-patch-commonsnet</artifactId>
+ <name>Gerrit Code Review - Patch commons-net</name>
+
+ <description>
+ Hacks to expose package-private data from commons-net to Gerrit
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-ssl</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java b/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
new file mode 100644
index 0000000000..e380df431e
--- /dev/null
+++ b/gerrit-patch-commonsnet/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
@@ -0,0 +1,207 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 org.apache.commons.net.smtp;
+
+import com.google.gerrit.util.ssl.BlindSSLSocketFactory;
+
+import org.apache.commons.codec.binary.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.SocketException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.net.ssl.SSLSocketFactory;
+
+public class AuthSMTPClient extends SMTPClient {
+ private static final Logger log = LoggerFactory.getLogger(AuthSMTPClient.class);
+ private static final String UTF_8 = "UTF-8";
+
+ private String authTypes;
+ private Set<String> allowedRcptTo;
+
+ public AuthSMTPClient(final String charset) {
+ super(charset);
+ }
+
+ public void enableSSL(final boolean verify) {
+ _socketFactory_ = sslFactory(verify);
+ }
+
+ public boolean startTLS(final String hostname, final int port, final boolean verify)
+ throws SocketException, IOException {
+ if (sendCommand("STARTTLS") != 220) {
+ return false;
+ }
+
+ _socket_ = sslFactory(verify).createSocket(_socket_, hostname, port, true);
+ _connectAction_();
+ return true;
+ }
+
+ private static SSLSocketFactory sslFactory(final boolean verify) {
+ if (verify) {
+ return (SSLSocketFactory) SSLSocketFactory.getDefault();
+ } else {
+ return (SSLSocketFactory) BlindSSLSocketFactory.getDefault();
+ }
+ }
+
+ public void setAllowRcpt(final String[] allowed) {
+ if (allowed != null && allowed.length > 0) {
+ if (allowedRcptTo == null) {
+ allowedRcptTo = new HashSet<String>();
+ }
+ for (final String addr : allowed) {
+ allowedRcptTo.add(addr);
+ }
+ }
+ }
+
+ @Override
+ public int rcpt(final String forwardPath) throws IOException {
+ if (allowRcpt(forwardPath)) {
+ return super.rcpt(forwardPath);
+ } else {
+ log.warn("Not emailing " + forwardPath + " (prohibited by allowrcpt)");
+ return SMTPReply.ACTION_OK;
+ }
+ }
+
+ private boolean allowRcpt(String addr) {
+ if (allowedRcptTo == null) {
+ return true;
+ }
+ if (addr.startsWith("<") && addr.endsWith(">")) {
+ addr = addr.substring(1, addr.length() - 1);
+ }
+ if (allowedRcptTo.contains(addr)) {
+ return true;
+ }
+ final int at = addr.indexOf('@');
+ if (at > 0) {
+ return allowedRcptTo.contains(addr.substring(at))
+ || allowedRcptTo.contains(addr.substring(at + 1));
+ }
+ return false;
+ }
+
+ @Override
+ public String[] getReplyStrings() {
+ return _replyLines.toArray(new String[_replyLines.size()]);
+ }
+
+ @Override
+ public boolean login() throws IOException {
+ final String name = getLocalAddress().getHostName();
+ if (name == null) {
+ return false;
+ }
+
+ boolean ok = SMTPReply.isPositiveCompletion(sendCommand("EHLO", name));
+ authTypes = "";
+ for (String line : getReplyStrings()) {
+ if (line != null && (line.startsWith("250 AUTH ") || line.startsWith("250-AUTH "))) {
+ authTypes = line;
+ break;
+ }
+ }
+
+ return ok;
+ }
+
+ public boolean auth(String smtpUser, String smtpPass) throws IOException {
+ List<String> types = Arrays.asList(authTypes.split(" "));
+ if ("".equals(authTypes)) {
+ // Server didn't advertise authentication support.
+ //
+ return true;
+ }
+
+ if (smtpPass == null) {
+ smtpPass = "";
+ }
+ if (types.contains("CRAM-SHA1")) {
+ return authCram(smtpUser, smtpPass, "SHA1");
+ }
+ if (types.contains("CRAM-MD5")) {
+ return authCram(smtpUser, smtpPass, "MD5");
+ }
+ if (types.contains("PLAIN")) {
+ return authPlain(smtpUser, smtpPass);
+ }
+
+ throw new IOException("Unsupported AUTH: " + authTypes);
+ }
+
+ private boolean authCram(String smtpUser, String smtpPass, String alg)
+ throws UnsupportedEncodingException, IOException {
+ final String macName = "Hmac" + alg;
+ if (sendCommand("AUTH", "CRAM-" + alg) != 334) {
+ return false;
+ }
+
+ final String enc = getReplyStrings()[0].split(" ", 2)[1];
+ final byte[] nonce = Base64.decodeBase64(enc.getBytes(UTF_8));
+ final String sec;
+ try {
+ Mac mac = Mac.getInstance(macName);
+ mac.init(new SecretKeySpec(smtpPass.getBytes(UTF_8), macName));
+ sec = toHex(mac.doFinal(nonce));
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("Cannot use CRAM-" + alg, e);
+ } catch (InvalidKeyException e) {
+ throw new IOException("Cannot use CRAM-" + alg, e);
+ }
+
+ String token = smtpUser + ' ' + sec;
+ String cmd = encodeBase64(token.getBytes(UTF_8));
+ return SMTPReply.isPositiveCompletion(sendCommand(cmd));
+ }
+
+ private static final char[] hexchar =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ private String toHex(final byte[] b) {
+ final StringBuilder sec = new StringBuilder();
+ for (int i = 0; i < b.length; i++) {
+ final int u = (b[i] >> 4) & 0xf;
+ final int l = b[i] & 0xf;
+ sec.append(hexchar[u]);
+ sec.append(hexchar[l]);
+ }
+ return sec.toString();
+ }
+
+ private boolean authPlain(String smtpUser, String smtpPass) throws UnsupportedEncodingException,
+ IOException {
+ String token = '\0' + smtpUser + '\0' + smtpPass;
+ String cmd = "PLAIN " + encodeBase64(token.getBytes(UTF_8));
+ return SMTPReply.isPositiveCompletion(sendCommand("AUTH", cmd));
+ }
+
+ private static String encodeBase64(final byte[] data) throws UnsupportedEncodingException {
+ return new String(Base64.encodeBase64(data), UTF_8);
+ }
+}
diff --git a/gerrit-patch-gwtexpui/.gitignore b/gerrit-patch-gwtexpui/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-patch-gwtexpui/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-patch-gwtexpui/.settings/org.eclipse.core.resources.prefs b/gerrit-patch-gwtexpui/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-patch-gwtexpui/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-patch-gwtexpui/.settings/org.eclipse.core.runtime.prefs b/gerrit-patch-gwtexpui/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-patch-gwtexpui/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.core.prefs b/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.ui.prefs b/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-patch-gwtexpui/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-patch-gwtexpui/pom.xml b/gerrit-patch-gwtexpui/pom.xml
new file mode 100644
index 0000000000..f738ac8913
--- /dev/null
+++ b/gerrit-patch-gwtexpui/pom.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-patch-gwtexpui</artifactId>
+ <name>Gerrit Code Review - Patch gwtexpui</name>
+
+ <description>
+ Hacks to expose package-private data from gwtexpui to Gerrit
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/PrettyFormatter.gwt.xml b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/PrettyFormatter.gwt.xml
new file mode 100644
index 0000000000..b0679c3285
--- /dev/null
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/PrettyFormatter.gwt.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2008 The Android Open Source Project
+
+ 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.
+-->
+<module>
+ <stylesheet src='prettify20090521/prettify.css' />
+ <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
+</module>
diff --git a/src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java
index e8bec8f70c..e8bec8f70c 100644
--- a/src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/MultiLineStyle.java
diff --git a/src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java
index c026e4a6ee..c026e4a6ee 100644
--- a/src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/PrettyFormatter.java
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-css.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-css.js
index 813a2cd291..813a2cd291 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-css.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-css.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-hs.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-hs.js
index ed65d9738b..ed65d9738b 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-hs.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-hs.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-lisp.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lisp.js
index b8bfe9825b..b8bfe9825b 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-lisp.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lisp.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-lua.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lua.js
index 6a1f681c22..6a1f681c22 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-lua.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-lua.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-ml.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-ml.js
index 774f7a0fe5..774f7a0fe5 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-ml.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-ml.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-proto.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-proto.js
index 03a02e409b..03a02e409b 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-proto.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-proto.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-sql.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-sql.js
index 00e9361932..00e9361932 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-sql.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-sql.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-vb.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-vb.js
index efe2380865..efe2380865 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-vb.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-vb.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/lang-wiki.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-wiki.js
index 87637bb12f..87637bb12f 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/lang-wiki.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/lang-wiki.js
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/prettify.css b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.css
index 351152b82d..351152b82d 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/prettify.css
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.css
diff --git a/src/main/java/com/google/gerrit/public/prettify20090521/prettify.js b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.js
index 1e4ee30692..1e4ee30692 100644
--- a/src/main/java/com/google/gerrit/public/prettify20090521/prettify.js
+++ b/gerrit-patch-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/public/prettify20090521/prettify.js
diff --git a/gerrit-patch-jgit/.gitignore b/gerrit-patch-jgit/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-patch-jgit/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs b/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-patch-jgit/.settings/org.eclipse.core.runtime.prefs b/gerrit-patch-jgit/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-patch-jgit/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-patch-jgit/.settings/org.eclipse.jdt.core.prefs b/gerrit-patch-jgit/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-patch-jgit/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-patch-jgit/.settings/org.eclipse.jdt.ui.prefs b/gerrit-patch-jgit/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-patch-jgit/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-patch-jgit/pom.xml b/gerrit-patch-jgit/pom.xml
new file mode 100644
index 0000000000..76878e03fa
--- /dev/null
+++ b/gerrit-patch-jgit/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-patch-jgit</artifactId>
+ <name>Gerrit Code Review - Patch JGit</name>
+
+ <description>
+ Hacks to expose package-private data from JGit to Gerrit
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/eclipse/jgit/JGit.gwt.xml b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/JGit.gwt.xml
index c4d3a9f5e1..c4d3a9f5e1 100644
--- a/src/main/java/org/eclipse/jgit/JGit.gwt.xml
+++ b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/JGit.gwt.xml
diff --git a/src/main/java/org/eclipse/jgit/diff/EditDeserializer.java b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/EditDeserializer.java
index b3eb9e0cf0..b3eb9e0cf0 100644
--- a/src/main/java/org/eclipse/jgit/diff/EditDeserializer.java
+++ b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/EditDeserializer.java
diff --git a/src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java
index 1b138fb204..1b138fb204 100644
--- a/src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java
+++ b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/diff/Edit_JsonSerializer.java
diff --git a/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java
new file mode 100644
index 0000000000..68a0fd4309
--- /dev/null
+++ b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 org.eclipse.jgit.lib;
+
+import org.eclipse.jgit.util.NB;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class ObjectIdSerialization {
+ public static void writeCanBeNull(final OutputStream out, final AnyObjectId id)
+ throws IOException {
+ if (id != null) {
+ out.write((byte)1);
+ writeNotNull(out, id);
+ } else {
+ out.write((byte)0);
+ }
+ }
+
+ public static void writeNotNull(final OutputStream out, final AnyObjectId id)
+ throws IOException {
+ id.copyRawTo(out);
+ }
+
+ public static ObjectId readCanBeNull(final InputStream in) throws IOException {
+ switch (in.read()) {
+ case 0:
+ return null;
+ case 1:
+ return readNotNull(in);
+ default:
+ throw new IOException("Invalid flag before ObjectId");
+ }
+ }
+
+ public static ObjectId readNotNull(final InputStream in) throws IOException {
+ final byte[] b = new byte[20];
+ NB.readFully(in, b, 0, 20);
+ return ObjectId.fromRaw(b);
+ }
+
+ private ObjectIdSerialization() {
+ }
+}
diff --git a/src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java
index 7d12e47e3a..7d12e47e3a 100644
--- a/src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java
+++ b/gerrit-patch-jgit/src/main/java/org/eclipse/jgit/lib/WindowCacheStatAccessor.java
diff --git a/gerrit-pgm/.gitignore b/gerrit-pgm/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-pgm/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-pgm/.settings/org.eclipse.core.resources.prefs b/gerrit-pgm/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-pgm/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-pgm/.settings/org.eclipse.core.runtime.prefs b/gerrit-pgm/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-pgm/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-pgm/.settings/org.eclipse.jdt.core.prefs b/gerrit-pgm/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-pgm/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-pgm/.settings/org.eclipse.jdt.ui.prefs b/gerrit-pgm/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-pgm/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-pgm/pom.xml b/gerrit-pgm/pom.xml
new file mode 100644
index 0000000000..485de55135
--- /dev/null
+++ b/gerrit-pgm/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-pgm</artifactId>
+ <name>Gerrit Code Review - Pgm</name>
+
+ <description>
+ Command line executables
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-cli</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-server</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-sshd</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/AbstractProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/AbstractProgram.java
new file mode 100644
index 0000000000..4ec9756256
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/AbstractProgram.java
@@ -0,0 +1,99 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.pgm;
+
+
+import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.Option;
+
+import java.io.StringWriter;
+import java.util.Collections;
+
+/** Base class for command line invocations of Gerrit Code Review. */
+public abstract class AbstractProgram {
+ private final Object sleepLock = new Object();
+ private boolean running = true;
+
+ @Option(name = "--help", usage = "display this help text", aliases = {"-h"})
+ private boolean help;
+
+ private String getName() {
+ String n = getClass().getName();
+ int dot = n.lastIndexOf('.');
+ if (0 < dot) {
+ n = n.substring(dot + 1);
+ }
+ return n.toLowerCase();
+ }
+
+ public final int main(final String[] argv) throws Exception {
+ final Injector empty = emptyInjector();
+ final CmdLineParser clp = new CmdLineParser(empty, this);
+ try {
+ clp.parseArgument(argv);
+ } catch (CmdLineException err) {
+ if (!help) {
+ System.err.println("fatal: " + err.getMessage());
+ return 1;
+ }
+ }
+
+ if (help) {
+ final StringWriter msg = new StringWriter();
+ msg.write(getName());
+ clp.printSingleLineUsage(msg, null);
+ msg.write('\n');
+
+ msg.write('\n');
+ clp.printUsage(msg, null);
+ msg.write('\n');
+ System.err.println(msg.toString());
+ return 1;
+ }
+
+ return run();
+ }
+
+ private static Injector emptyInjector() {
+ return Guice.createInjector(Collections.<Module> emptyList());
+ }
+
+ /** Method that never returns, e.g. to keep a daemon running. */
+ protected int never() {
+ synchronized (sleepLock) {
+ while (running) {
+ try {
+ sleepLock.wait(60 * 60 * 1000L);
+ } catch (InterruptedException e) {
+ continue;
+ }
+ }
+ return 0;
+ }
+ }
+
+ /**
+ * Run this program's logic, returning the command exit status.
+ * <p>
+ * When this method completes, the JVM is terminated. To keep the JVM running,
+ * use {@code return never()}.
+ */
+ public abstract int run() throws Exception;
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/CreateSchema.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/CreateSchema.java
new file mode 100644
index 0000000000..ea2604963b
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/CreateSchema.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.pgm;
+
+import static com.google.inject.Stage.PRODUCTION;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SchemaVersion;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.config.DatabaseModule;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * Creates the Gerrit 2 database schema.
+ */
+public class CreateSchema extends AbstractProgram {
+ @Inject
+ private SystemConfig systemConfig;
+
+ @Inject
+ private SchemaFactory<ReviewDb> schema;
+
+ @Override
+ public int run() throws Exception {
+ final Injector injector =
+ Guice.createInjector(PRODUCTION, new DatabaseModule());
+ injector.injectMembers(this);
+
+ final SchemaVersion sv;
+ final ReviewDb db = schema.open();
+ try {
+ sv = db.schemaVersion().get(new SchemaVersion.Key());
+ } finally {
+ db.close();
+ }
+ if (sv == null) {
+ System.err.println("Schema failed to initialize");
+ return 1;
+ }
+
+ System.out.println("Gerrit Code Review initialized.");
+ System.out.println("Current settings:");
+ System.out.println(" schema version = " + sv.versionNbr);
+ System.out.println(" admin group = " + systemConfig.adminGroupId);
+ System.out.println(" site_path = " + systemConfig.sitePath);
+ System.out.println();
+ return 0;
+ }
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
new file mode 100644
index 0000000000..558a1b1071
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.pgm;
+
+import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.server.config.GerritGlobalModule;
+import com.google.gerrit.sshd.SshDaemon;
+import com.google.gerrit.sshd.SshModule;
+import com.google.gerrit.sshd.commands.MasterCommandModule;
+import com.google.gerrit.sshd.commands.SlaveCommandModule;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+import org.kohsuke.args4j.Option;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Run only the SSH daemon portions of Gerrit. */
+public class Daemon extends AbstractProgram {
+
+ @Option(name = "--slave", usage = "support fetch only")
+ boolean slave;
+
+ @Override
+ public int run() throws Exception {
+ Injector sysInjector = GerritGlobalModule.createInjector();
+ Injector sshInjector = createSshInjector(sysInjector);
+ sysInjector.getInstance(CachePool.class).start();
+ sshInjector.getInstance(SshDaemon.class).start();
+ return never();
+ }
+
+ private Injector createSshInjector(final Injector sysInjector) {
+ final List<Module> modules = new ArrayList<Module>();
+ modules.add(new SshModule());
+ if (slave) {
+ modules.add(new SlaveCommandModule());
+ } else {
+ modules.add(new MasterCommandModule());
+ }
+ return sysInjector.createChildInjector(modules);
+ }
+}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Version.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Version.java
new file mode 100644
index 0000000000..3bf9cf8605
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Version.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.pgm;
+
+/** Display the version of Gerrit. */
+public class Version extends AbstractProgram {
+ @Override
+ public int run() throws Exception {
+ final String v = com.google.gerrit.common.Version.getVersion();
+ if (v == null) {
+ System.err.println("fatal: version unavailable");
+ return 1;
+ }
+ System.out.println("gerrit version " + v);
+ return 0;
+ }
+}
diff --git a/gerrit-reviewdb/.gitignore b/gerrit-reviewdb/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-reviewdb/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs b/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-reviewdb/.settings/org.eclipse.core.runtime.prefs b/gerrit-reviewdb/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-reviewdb/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-reviewdb/.settings/org.eclipse.jdt.core.prefs b/gerrit-reviewdb/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-reviewdb/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-reviewdb/.settings/org.eclipse.jdt.ui.prefs b/gerrit-reviewdb/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-reviewdb/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-reviewdb/pom.xml b/gerrit-reviewdb/pom.xml
new file mode 100644
index 0000000000..638d86e45c
--- /dev/null
+++ b/gerrit-reviewdb/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-reviewdb</artifactId>
+ <name>Gerrit Code Review - ReviewDB</name>
+
+ <description>
+ Database schema definition and interface.
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>gwtorm</groupId>
+ <artifactId>gwtorm</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/ReviewDB.gwt.xml b/gerrit-reviewdb/src/main/java/com/google/gerrit/ReviewDB.gwt.xml
new file mode 100644
index 0000000000..c5f8912705
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/ReviewDB.gwt.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2009 The Android Open Source Project
+
+ 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.
+-->
+<module>
+ <inherits name='com.google.gwtorm.GWTORM'/>
+ <source path='reviewdb' />
+</module>
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AbstractAgreement.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AbstractAgreement.java
new file mode 100644
index 0000000000..987fc4ba16
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AbstractAgreement.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import java.sql.Timestamp;
+
+/** Base for {@link AccountAgreement} or {@link AccountGroupAgreement}. */
+public interface AbstractAgreement {
+ public static enum Status {
+ NEW('n'),
+
+ VERIFIED('V'),
+
+ REJECTED('R');
+
+ private final char code;
+
+ private Status(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static Status forCode(final char c) {
+ for (final Status s : Status.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ public ContributorAgreement.Id getAgreementId();
+
+ public Timestamp getAcceptedOn();
+
+ public Status getStatus();
+
+ public Timestamp getReviewedOn();
+
+ public Account.Id getReviewedBy();
+
+ public String getReviewComments();
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Account.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Account.java
new file mode 100644
index 0000000000..7b45eee7e5
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Account.java
@@ -0,0 +1,207 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+
+import java.sql.Timestamp;
+
+/**
+ * Information about a single user.
+ * <p>
+ * A user may have multiple identities they can use to login to Gerrit (see
+ * {@link AccountExternalId}), but in such cases they always map back to a
+ * single Account entity.
+ *<p>
+ * Entities "owned" by an Account (that is, their primary key contains the
+ * {@link Account.Id} key as part of their key structure):
+ * <ul>
+ * <li>{@link AccountAgreement}: any record of the user's acceptance of a
+ * predefined {@link ContributorAgreement}. Multiple records indicate
+ * potentially multiple agreements, especially if agreements must be retired and
+ * replaced with new agreements.</li>
+ *
+ * <li>{@link AccountExternalId}: OpenID identities and email addresses known to
+ * be registered to this user. Multiple records can exist when the user has more
+ * than one public identity, such as a work and a personal email address.</li>
+ *
+ * <li>{@link AccountGroupMember}: membership of the user in a specific human
+ * managed {@link AccountGroup}. Multiple records can exist when the user is a
+ * member of more than one group.</li>
+ *
+ * <li>{@link AccountProjectWatch}: user's email settings related to a specific
+ * {@link Project}. One record per project the user is interested in tracking.</li>
+ *
+ * <li>{@link AccountSshKey}: user's public SSH keys, for authentication through
+ * the internal SSH daemon. One record per SSH key uploaded by the user, keys
+ * are checked in random order until a match is found.</li>
+ *
+ * <li>{@link StarredChange}: user has starred the change, tracking
+ * notifications of updates on that change, or just book-marking it for faster
+ * future reference. One record per starred change.</li>
+ * </ul>
+ */
+public final class Account {
+ public static enum FieldName {
+ FULL_NAME, SSH_USER_NAME, REGISTER_NEW_EMAIL;
+ }
+
+ public static final String SSH_USER_NAME_PATTERN_FIRST = "[a-zA-Z]";
+ public static final String SSH_USER_NAME_PATTERN_REST = "[a-zA-Z0-9._-]";
+ public static final String SSH_USER_NAME_PATTERN_LAST = "[a-zA-Z0-9]";
+
+ /** Regular expression that {@link #sshUserName} must match. */
+ public static final String SSH_USER_NAME_PATTERN = "^" + //
+ SSH_USER_NAME_PATTERN_FIRST + //
+ SSH_USER_NAME_PATTERN_REST + "*" + //
+ SSH_USER_NAME_PATTERN_LAST + //
+ "$";
+
+ /** Key local to Gerrit to identify a user. */
+ public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected int id;
+
+ protected Id() {
+ }
+
+ public Id(final int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ id = newValue;
+ }
+
+ /** Parse an Account.Id out of a string representation. */
+ public static Id parse(final String str) {
+ final Id r = new Id();
+ r.fromString(str);
+ return r;
+ }
+ }
+
+ @Column
+ protected Id accountId;
+
+ /** Date and time the user registered with the review server. */
+ @Column
+ protected Timestamp registeredOn;
+
+ /** Full name of the user ("Given-name Surname" style). */
+ @Column(notNull = false)
+ protected String fullName;
+
+ /** Email address the user prefers to be contacted through. */
+ @Column(notNull = false)
+ protected String preferredEmail;
+
+ /** Username to authenticate as through SSH connections. */
+ @Column(notNull = false)
+ protected String sshUserName;
+
+ /** When did the user last give us contact information? Null if never. */
+ @Column(notNull = false)
+ protected Timestamp contactFiledOn;
+
+ /** This user's preferences */
+ @Column(name = Column.NONE)
+ protected AccountGeneralPreferences generalPreferences;
+
+ protected Account() {
+ }
+
+ /**
+ * Create a new account.
+ *
+ * @param newId unique id, see {@link ReviewDb#nextAccountId()}.
+ */
+ public Account(final Account.Id newId) {
+ accountId = newId;
+ registeredOn = new Timestamp(System.currentTimeMillis());
+
+ generalPreferences = new AccountGeneralPreferences();
+ generalPreferences.resetToDefaults();
+ }
+
+ /** Get local id of this account, to link with in other entities */
+ public Account.Id getId() {
+ return accountId;
+ }
+
+ /** Get the full name of the user ("Given-name Surname" style). */
+ public String getFullName() {
+ return fullName;
+ }
+
+ /** Set the full name of the user ("Given-name Surname" style). */
+ public void setFullName(final String name) {
+ fullName = name;
+ }
+
+ /** Email address the user prefers to be contacted through. */
+ public String getPreferredEmail() {
+ return preferredEmail;
+ }
+
+ /** Set the email address the user prefers to be contacted through. */
+ public void setPreferredEmail(final String addr) {
+ preferredEmail = addr;
+ }
+
+ /** Get the name the user logins as through SSH. */
+ public String getSshUserName() {
+ return sshUserName;
+ }
+
+ /** Set the name the user logins as through SSH. */
+ public void setSshUserName(final String name) {
+ sshUserName = name;
+ }
+
+ /** Get the date and time the user first registered. */
+ public Timestamp getRegisteredOn() {
+ return registeredOn;
+ }
+
+ public AccountGeneralPreferences getGeneralPreferences() {
+ return generalPreferences;
+ }
+
+ public void setGeneralPreferences(final AccountGeneralPreferences p) {
+ generalPreferences = p;
+ }
+
+ public boolean isContactFiled() {
+ return contactFiledOn != null;
+ }
+
+ public Timestamp getContactFiledOn() {
+ return contactFiledOn;
+ }
+
+ public void setContactFiled() {
+ contactFiledOn = new Timestamp(System.currentTimeMillis());
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAccess.java
new file mode 100644
index 0000000000..051cfe7ba1
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAccess.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.SecondaryKey;
+
+/** Access interface for {@link Account}. */
+public interface AccountAccess extends Access<Account, Account.Id> {
+ /** Locate an account by our locally generated identity. */
+ @PrimaryKey("accountId")
+ Account get(Account.Id key) throws OrmException;
+
+ @Query("WHERE preferredEmail = ? LIMIT 2")
+ ResultSet<Account> byPreferredEmail(String email) throws OrmException;
+
+ @SecondaryKey("sshUserName")
+ Account bySshUserName(String userName) throws OrmException;
+
+ @Query("WHERE fullName = ? LIMIT 2")
+ ResultSet<Account> byFullName(String name) throws OrmException;
+
+ @Query("WHERE fullName >= ? AND fullName <= ? ORDER BY fullName LIMIT ?")
+ ResultSet<Account> suggestByFullName(String nameA, String nameB, int limit)
+ throws OrmException;
+
+ @Query("WHERE preferredEmail >= ? AND preferredEmail <= ? ORDER BY preferredEmail LIMIT ?")
+ ResultSet<Account> suggestByPreferredEmail(String nameA, String nameB,
+ int limit) throws OrmException;
+
+ @Query("WHERE sshUserName >= ? AND sshUserName <= ? ORDER BY sshUserName LIMIT ?")
+ ResultSet<Account> suggestBySshUserName(String nameA, String nameB, int limit)
+ throws OrmException;
+
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreement.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreement.java
new file mode 100644
index 0000000000..8de6180add
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreement.java
@@ -0,0 +1,118 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+import java.sql.Timestamp;
+
+/** Electronic acceptance of a {@link ContributorAgreement} by {@link Account} */
+public final class AccountAgreement implements AbstractAgreement {
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected ContributorAgreement.Id claId;
+
+ protected Key() {
+ accountId = new Account.Id();
+ claId = new ContributorAgreement.Id();
+ }
+
+ public Key(final Account.Id account, final ContributorAgreement.Id cla) {
+ this.accountId = account;
+ this.claId = cla;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {claId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ @Column
+ protected Timestamp acceptedOn;
+
+ @Column
+ protected char status;
+
+ @Column(notNull = false)
+ protected Account.Id reviewedBy;
+
+ @Column(notNull = false)
+ protected Timestamp reviewedOn;
+
+ @Column(notNull = false, length = Integer.MAX_VALUE)
+ protected String reviewComments;
+
+ protected AccountAgreement() {
+ }
+
+ public AccountAgreement(final AccountAgreement.Key k) {
+ key = k;
+ acceptedOn = new Timestamp(System.currentTimeMillis());
+ status = Status.NEW.getCode();
+ }
+
+ public AccountAgreement.Key getKey() {
+ return key;
+ }
+
+ public ContributorAgreement.Id getAgreementId() {
+ return key.claId;
+ }
+
+ public Timestamp getAcceptedOn() {
+ return acceptedOn;
+ }
+
+ public Status getStatus() {
+ return Status.forCode(status);
+ }
+
+ public Timestamp getReviewedOn() {
+ return reviewedOn;
+ }
+
+ public Account.Id getReviewedBy() {
+ return reviewedBy;
+ }
+
+ public String getReviewComments() {
+ return reviewComments;
+ }
+
+ public void setReviewComments(final String s) {
+ reviewComments = s;
+ }
+
+ public void review(final Status newStatus, final Account.Id by) {
+ status = newStatus.getCode();
+ reviewedBy = by;
+ reviewedOn = new Timestamp(System.currentTimeMillis());
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java
new file mode 100644
index 0000000000..b9f8905a66
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountAgreementAccess.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountAgreementAccess extends
+ Access<AccountAgreement, AccountAgreement.Key> {
+ @PrimaryKey("key")
+ AccountAgreement get(AccountAgreement.Key key) throws OrmException;
+
+ @Query("WHERE key.accountId = ? ORDER BY acceptedOn DESC")
+ ResultSet<AccountAgreement> byAccount(Account.Id id) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalId.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalId.java
new file mode 100644
index 0000000000..71f12ca4fe
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalId.java
@@ -0,0 +1,158 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+import java.sql.Timestamp;
+import java.util.Collection;
+
+/** Association of an external account identifier to a local {@link Account}. */
+public final class AccountExternalId {
+ public static final String SCHEME_GERRIT = "gerrit:";
+ public static final String SCHEME_MAILTO = "mailto:";
+ public static final String LEGACY_GAE = "Google Account ";
+
+ public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected String externalId;
+
+ protected Key() {
+ }
+
+ public Key(final String e) {
+ externalId = e;
+ }
+
+ @Override
+ public String get() {
+ return externalId;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ externalId = newValue;
+ }
+ }
+
+ /**
+ * Select the most recently used identity from a list of identities.
+ *
+ * @param all all known identities
+ * @return most recently used login identity; null if none matches.
+ */
+ public static AccountExternalId mostRecent(Collection<AccountExternalId> all) {
+ AccountExternalId mostRecent = null;
+ for (final AccountExternalId e : all) {
+ final Timestamp lastUsed = e.getLastUsedOn();
+ if (lastUsed == null) {
+ // Identities without logins have never been used, so
+ // they can't be the most recent.
+ //
+ continue;
+ }
+
+ if (e.isScheme(SCHEME_MAILTO)) {
+ // Don't ever consider an email address as a "recent login"
+ //
+ continue;
+ }
+
+ if (mostRecent == null
+ || lastUsed.getTime() > mostRecent.getLastUsedOn().getTime()) {
+ mostRecent = e;
+ }
+ }
+ return mostRecent;
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column(notNull = false)
+ protected String emailAddress;
+
+ @Column(notNull = false)
+ protected Timestamp lastUsedOn;
+
+ /** <i>computed value</i> is this identity trusted by the site administrator? */
+ protected boolean trusted;
+
+ protected AccountExternalId() {
+ }
+
+ /**
+ * Create a new binding to an external identity.
+ *
+ * @param who the account this binds to.
+ * @param k the binding key.
+ */
+ public AccountExternalId(final Account.Id who, final AccountExternalId.Key k) {
+ accountId = who;
+ key = k;
+ }
+
+ public AccountExternalId.Key getKey() {
+ return key;
+ }
+
+ /** Get local id of this account, to link with in other entities */
+ public Account.Id getAccountId() {
+ return accountId;
+ }
+
+ public String getExternalId() {
+ return key.externalId;
+ }
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public void setEmailAddress(final String e) {
+ emailAddress = e;
+ }
+
+ public Timestamp getLastUsedOn() {
+ return lastUsedOn;
+ }
+
+ public void setLastUsedOn() {
+ lastUsedOn = new Timestamp(System.currentTimeMillis());
+ }
+
+ public boolean isScheme(final String scheme) {
+ final String id = getExternalId();
+ return id != null && id.startsWith(scheme);
+ }
+
+ public String getSchemeRest(final String scheme) {
+ return isScheme(scheme) ? getExternalId().substring(scheme.length()) : null;
+ }
+
+ public boolean isTrusted() {
+ return trusted;
+ }
+
+ public void setTrusted(final boolean t) {
+ trusted = t;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java
new file mode 100644
index 0000000000..02c820437c
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountExternalIdAccess extends
+ Access<AccountExternalId, AccountExternalId.Key> {
+ @PrimaryKey("key")
+ AccountExternalId get(AccountExternalId.Key key) throws OrmException;
+
+ @Query("WHERE accountId = ?")
+ ResultSet<AccountExternalId> byAccount(Account.Id id) throws OrmException;
+
+ @Query("WHERE accountId = ? AND emailAddress = ?")
+ ResultSet<AccountExternalId> byAccountEmail(Account.Id id, String email)
+ throws OrmException;
+
+ @Query("WHERE emailAddress = ?")
+ ResultSet<AccountExternalId> byEmailAddress(String email) throws OrmException;
+
+ @Query("WHERE emailAddress >= ? AND emailAddress <= ? ORDER BY emailAddress LIMIT ?")
+ ResultSet<AccountExternalId> suggestByEmailAddress(String emailA,
+ String emailB, int limit) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java
new file mode 100644
index 0000000000..cea77a001b
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGeneralPreferences.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+
+/** Preferences about a single user. */
+public final class AccountGeneralPreferences {
+ /** Default number of lines of context. */
+ public static final short DEFAULT_CONTEXT = 10;
+
+ /** Context setting to display the entire file. */
+ public static final short WHOLE_FILE_CONTEXT = -1;
+
+ /** Typical valid choices for the default context setting. */
+ public static final short[] CONTEXT_CHOICES =
+ {3, 10, 25, 50, 75, 100, WHOLE_FILE_CONTEXT};
+
+ /** Default number of items to display per page. */
+ public static final short DEFAULT_PAGESIZE = 25;
+
+ /** Valid choices for the page size. */
+ public static final short[] PAGESIZE_CHOICES = {10, 25, 50, 100};
+
+ /** Default number of lines of context when viewing a patch. */
+ @Column
+ protected short defaultContext;
+
+ /** Number of changes to show in a screen. */
+ @Column
+ protected short maximumPageSize;
+
+ /** Should the site header be displayed when logged in ? */
+ @Column
+ protected boolean showSiteHeader;
+
+ /** Should the Flash helper movie be used to copy text to the clipboard? */
+ @Column
+ protected boolean useFlashClipboard;
+
+ public AccountGeneralPreferences() {
+ }
+
+ /** Get the default number of lines of context when viewing a patch. */
+ public short getDefaultContext() {
+ return defaultContext;
+ }
+
+ /** Set the number of lines of context when viewing a patch. */
+ public void setDefaultContext(final short s) {
+ defaultContext = s;
+ }
+
+ public short getMaximumPageSize() {
+ return maximumPageSize;
+ }
+
+ public void setMaximumPageSize(final short s) {
+ maximumPageSize = s;
+ }
+
+ public boolean isShowSiteHeader() {
+ return showSiteHeader;
+ }
+
+ public void setShowSiteHeader(final boolean b) {
+ showSiteHeader = b;
+ }
+
+ public boolean isUseFlashClipboard() {
+ return useFlashClipboard;
+ }
+
+ public void setUseFlashClipboard(final boolean b) {
+ useFlashClipboard = b;
+ }
+
+ public void resetToDefaults() {
+ defaultContext = DEFAULT_CONTEXT;
+ maximumPageSize = DEFAULT_PAGESIZE;
+ showSiteHeader = true;
+ useFlashClipboard = true;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
new file mode 100644
index 0000000000..ea16d91a34
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
@@ -0,0 +1,229 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+import com.google.gwtorm.client.StringKey;
+
+/** Named group of one or more accounts, typically used for access controls. */
+public final class AccountGroup {
+ /** Group name key */
+ public static class NameKey extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(length = 40)
+ protected String name;
+
+ protected NameKey() {
+ }
+
+ public NameKey(final String n) {
+ name = n;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ name = newValue;
+ }
+ }
+
+ /** Distinguished name, within organization directory server. */
+ public static class ExternalNameKey extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected String name;
+
+ protected ExternalNameKey() {
+ }
+
+ public ExternalNameKey(final String n) {
+ name = n;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ name = newValue;
+ }
+ }
+
+ /** Synthetic key to link to within the database */
+ public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected int id;
+
+ protected Id() {
+ }
+
+ public Id(final int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ id = newValue;
+ }
+
+ /** Parse an AccountGroup.Id out of a string representation. */
+ public static Id parse(final String str) {
+ final Id r = new Id();
+ r.fromString(str);
+ return r;
+ }
+ }
+
+ public static enum Type {
+ /**
+ * System defined and managed group, e.g. anonymous users.
+ * <p>
+ * These groups must be explicitly named by {@link SystemConfig} and are
+ * specially handled throughout the code. In UI contexts their membership is
+ * not displayed. When computing effective group membership for any given
+ * user account, these groups are automatically handled using specialized
+ * branch conditions.
+ */
+ SYSTEM,
+
+ /**
+ * Group defined within our database.
+ * <p>
+ * An internal group has its membership fully enumerated in the database.
+ * The membership can be viewed and edited through the web UI by any user
+ * who is a member of the owner group. These groups are not treated special
+ * in the code.
+ */
+ INTERNAL,
+
+ /**
+ * Group defined by external LDAP database.
+ * <p>
+ * A group whose membership is determined by the LDAP directory that we
+ * connect to for user and group information. In UI contexts the membership
+ * of the group is not displayed, as it may be exceedingly large, or might
+ * contain users who have never logged into this server before (and thus
+ * have no matching account record). Adding or removing users from an LDAP
+ * group requires making edits through the LDAP directory, and cannot be
+ * done through our UI.
+ */
+ LDAP;
+ }
+
+ /** Unique name of this group within the system. */
+ @Column
+ protected NameKey name;
+
+ /** Unique identity, to link entities as {@link #name} can change. */
+ @Column
+ protected Id groupId;
+
+ /**
+ * Identity of the group whose members can manage this group.
+ * <p>
+ * This can be a self-reference to indicate the group's members manage itself.
+ */
+ @Column
+ protected Id ownerGroupId;
+
+ /** A textual description of the group's purpose. */
+ @Column(length = Integer.MAX_VALUE, notNull = false)
+ protected String description;
+
+ /** Is the membership managed by some external means? */
+ @Column(length = 8)
+ protected String groupType;
+
+ /** Distinguished name in the directory server. */
+ @Column(notNull = false)
+ protected ExternalNameKey externalName;
+
+ protected AccountGroup() {
+ }
+
+ public AccountGroup(final AccountGroup.NameKey newName,
+ final AccountGroup.Id newId) {
+ name = newName;
+ groupId = newId;
+ ownerGroupId = groupId;
+ setType(Type.INTERNAL);
+ }
+
+ public AccountGroup.Id getId() {
+ return groupId;
+ }
+
+ public String getName() {
+ return name.get();
+ }
+
+ public AccountGroup.NameKey getNameKey() {
+ return name;
+ }
+
+ public void setNameKey(final AccountGroup.NameKey nameKey) {
+ name = nameKey;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String d) {
+ description = d;
+ }
+
+ public AccountGroup.Id getOwnerGroupId() {
+ return ownerGroupId;
+ }
+
+ public void setOwnerGroupId(final AccountGroup.Id id) {
+ ownerGroupId = id;
+ }
+
+ public Type getType() {
+ return Type.valueOf(groupType);
+ }
+
+ public void setType(final Type t) {
+ groupType = t.name();
+ }
+
+ public ExternalNameKey getExternalNameKey() {
+ return externalName;
+ }
+
+ public void setExternalNameKey(final ExternalNameKey k) {
+ externalName = k;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
new file mode 100644
index 0000000000..231ff0ab3b
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
@@ -0,0 +1,45 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.SecondaryKey;
+
+public interface AccountGroupAccess extends
+ Access<AccountGroup, AccountGroup.Id> {
+ @PrimaryKey("groupId")
+ AccountGroup get(AccountGroup.Id id) throws OrmException;
+
+ @SecondaryKey("name")
+ AccountGroup get(AccountGroup.NameKey name) throws OrmException;
+
+ @SecondaryKey("externalName")
+ AccountGroup get(AccountGroup.ExternalNameKey name) throws OrmException;
+
+ @Query("ORDER BY name")
+ ResultSet<AccountGroup> all() throws OrmException;
+
+ @Query("WHERE ownerGroupId = ?")
+ ResultSet<AccountGroup> ownedByGroup(AccountGroup.Id groupId)
+ throws OrmException;
+
+ @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
+ ResultSet<AccountGroup> suggestByName(String nameA, String nameB, int limit)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreement.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreement.java
new file mode 100644
index 0000000000..30c54217b4
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreement.java
@@ -0,0 +1,114 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+import java.sql.Timestamp;
+
+/**
+ * Acceptance of a {@link ContributorAgreement} by an {@link AccountGroup}.
+ */
+public final class AccountGroupAgreement implements AbstractAgreement {
+ public static class Key extends CompoundKey<AccountGroup.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected AccountGroup.Id groupId;
+
+ @Column
+ protected ContributorAgreement.Id claId;
+
+ protected Key() {
+ groupId = new AccountGroup.Id();
+ claId = new ContributorAgreement.Id();
+ }
+
+ public Key(final AccountGroup.Id group, final ContributorAgreement.Id cla) {
+ this.groupId = group;
+ this.claId = cla;
+ }
+
+ @Override
+ public AccountGroup.Id getParentKey() {
+ return groupId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {claId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ @Column
+ protected Timestamp acceptedOn;
+
+ @Column
+ protected char status;
+
+ @Column(notNull = false)
+ protected Account.Id reviewedBy;
+
+ @Column(notNull = false)
+ protected Timestamp reviewedOn;
+
+ @Column(notNull = false, length = Integer.MAX_VALUE)
+ protected String reviewComments;
+
+ protected AccountGroupAgreement() {
+ }
+
+ public AccountGroupAgreement(final AccountGroupAgreement.Key k) {
+ key = k;
+ acceptedOn = new Timestamp(System.currentTimeMillis());
+ status = Status.NEW.getCode();
+ }
+
+ public AccountGroupAgreement.Key getKey() {
+ return key;
+ }
+
+ public ContributorAgreement.Id getAgreementId() {
+ return key.claId;
+ }
+
+ public Timestamp getAcceptedOn() {
+ return acceptedOn;
+ }
+
+ public Status getStatus() {
+ return Status.forCode(status);
+ }
+
+ public Timestamp getReviewedOn() {
+ return reviewedOn;
+ }
+
+ public Account.Id getReviewedBy() {
+ return reviewedBy;
+ }
+
+ public String getReviewComments() {
+ return reviewComments;
+ }
+
+ public void setReviewComments(final String s) {
+ reviewComments = s;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java
new file mode 100644
index 0000000000..d4719341ea
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAgreementAccess.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountGroupAgreementAccess extends
+ Access<AccountGroupAgreement, AccountGroupAgreement.Key> {
+ @PrimaryKey("key")
+ AccountGroupAgreement get(AccountGroupAgreement.Key key) throws OrmException;
+
+ @Query("WHERE key.groupId = ? ORDER BY acceptedOn DESC")
+ ResultSet<AccountGroupAgreement> byGroup(AccountGroup.Id id)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMember.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMember.java
new file mode 100644
index 0000000000..47d7e44072
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMember.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+/** Membership of an {@link Account} in an {@link AccountGroup}. */
+public final class AccountGroupMember {
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected AccountGroup.Id groupId;
+
+ protected Key() {
+ accountId = new Account.Id();
+ groupId = new AccountGroup.Id();
+ }
+
+ public Key(final Account.Id a, final AccountGroup.Id g) {
+ accountId = a;
+ groupId = g;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ public AccountGroup.Id getAccountGroupId() {
+ return groupId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {groupId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ protected AccountGroupMember() {
+ }
+
+ public AccountGroupMember(final AccountGroupMember.Key k) {
+ key = k;
+ }
+
+ public AccountGroupMember.Key getKey() {
+ return key;
+ }
+
+ public Account.Id getAccountId() {
+ return key.accountId;
+ }
+
+ public AccountGroup.Id getAccountGroupId() {
+ return key.groupId;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAccess.java
new file mode 100644
index 0000000000..48a20e3fb6
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAccess.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountGroupMemberAccess extends
+ Access<AccountGroupMember, AccountGroupMember.Key> {
+ @PrimaryKey("key")
+ AccountGroupMember get(AccountGroupMember.Key key) throws OrmException;
+
+ @Query("WHERE key.accountId = ?")
+ ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException;
+
+ @Query("WHERE key.groupId = ?")
+ ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAudit.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAudit.java
new file mode 100644
index 0000000000..75d2c13578
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAudit.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+import java.sql.Timestamp;
+
+/** Membership of an {@link Account} in an {@link AccountGroup}. */
+public final class AccountGroupMemberAudit {
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected AccountGroup.Id groupId;
+
+ @Column
+ protected Timestamp addedOn;
+
+ protected Key() {
+ accountId = new Account.Id();
+ groupId = new AccountGroup.Id();
+ }
+
+ public Key(final Account.Id a, final AccountGroup.Id g, final Timestamp t) {
+ accountId = a;
+ groupId = g;
+ addedOn = t;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {groupId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ @Column
+ protected Account.Id addedBy;
+
+ @Column(notNull = false)
+ protected Account.Id removedBy;
+
+ @Column(notNull = false)
+ protected Timestamp removedOn;
+
+ protected AccountGroupMemberAudit() {
+ }
+
+ public AccountGroupMemberAudit(final AccountGroupMember m,
+ final Account.Id adder) {
+ final Account.Id who = m.getAccountId();
+ final AccountGroup.Id group = m.getAccountGroupId();
+ key = new AccountGroupMemberAudit.Key(who, group, now());
+ addedBy = adder;
+ }
+
+ public AccountGroupMemberAudit.Key getKey() {
+ return key;
+ }
+
+ public boolean isActive() {
+ return removedOn == null;
+ }
+
+ public void removed(final Account.Id deleter) {
+ removedBy = deleter;
+ removedOn = now();
+ }
+
+ public void removedLegacy() {
+ removedBy = addedBy;
+ removedOn = key.addedOn;
+ }
+
+ private static Timestamp now() {
+ return new Timestamp(System.currentTimeMillis());
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAuditAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAuditAccess.java
new file mode 100644
index 0000000000..b112059e56
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupMemberAuditAccess.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountGroupMemberAuditAccess extends
+ Access<AccountGroupMemberAudit, AccountGroupMemberAudit.Key> {
+ @PrimaryKey("key")
+ AccountGroupMemberAudit get(AccountGroupMemberAudit.Key key)
+ throws OrmException;
+
+ @Query("WHERE key.groupId = ? AND key.accountId = ?")
+ ResultSet<AccountGroupMemberAudit> byGroupAccount(AccountGroup.Id groupId,
+ Account.Id accountId) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReview.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReview.java
new file mode 100644
index 0000000000..5a3ebeefa7
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReview.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+/**
+ * An entity that keeps track of what user reviewed what patches.
+ */
+public final class AccountPatchReview {
+
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column(name = Column.NONE)
+ protected Patch.Key patchKey;
+
+ protected Key() {
+ accountId = new Account.Id();
+ patchKey = new Patch.Key();
+ }
+
+ public Key(final Patch.Key p, final Account.Id a) {
+ patchKey = p;
+ accountId = a;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ public Patch.Key getPatchKey() {
+ return patchKey;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {patchKey};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected AccountPatchReview.Key key;
+
+ protected AccountPatchReview() {
+ }
+
+ public AccountPatchReview(final Patch.Key k, final Account.Id a) {
+ key = new AccountPatchReview.Key(k, a);
+ }
+
+ public AccountPatchReview.Key getKey() {
+ return key;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReviewAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReviewAccess.java
new file mode 100644
index 0000000000..91e8837406
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountPatchReviewAccess.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountPatchReviewAccess
+ extends Access<AccountPatchReview, AccountPatchReview.Key> {
+ @PrimaryKey("key")
+ AccountPatchReview get(AccountPatchReview.Key id) throws OrmException;
+
+ @Query("WHERE key.accountId = ? AND key.patchKey.patchSetId = ?")
+ ResultSet<AccountPatchReview> byReviewer(Account.Id who, PatchSet.Id ps) throws OrmException;
+
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatch.java
new file mode 100644
index 0000000000..3217186796
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatch.java
@@ -0,0 +1,109 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+/** An {@link Account} interested in a {@link Project}. */
+public final class AccountProjectWatch {
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected Project.NameKey projectName;
+
+ protected Key() {
+ accountId = new Account.Id();
+ projectName = new Project.NameKey();
+ }
+
+ public Key(final Account.Id a, final Project.NameKey g) {
+ accountId = a;
+ projectName = g;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {projectName};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ /** Automatically send email notifications of new changes? */
+ @Column
+ protected boolean notifyNewChanges;
+
+ /** Automatically receive comments published to this project */
+ @Column
+ protected boolean notifyAllComments;
+
+ /** Automatically receive changes submitted to this project */
+ @Column
+ protected boolean notifySubmittedChanges;
+
+ protected AccountProjectWatch() {
+ }
+
+ public AccountProjectWatch(final AccountProjectWatch.Key k) {
+ key = k;
+ }
+
+ public AccountProjectWatch.Key getKey() {
+ return key;
+ }
+
+ public Account.Id getAccountId() {
+ return key.accountId;
+ }
+
+ public Project.NameKey getProjectNameKey() {
+ return key.projectName;
+ }
+
+ public boolean isNotifyNewChanges() {
+ return notifyNewChanges;
+ }
+
+ public void setNotifyNewChanges(final boolean a) {
+ notifyNewChanges = a;
+ }
+
+ public boolean isNotifyAllComments() {
+ return notifyAllComments;
+ }
+
+ public void setNotifyAllComments(final boolean a) {
+ notifyAllComments = a;
+ }
+
+ public boolean isNotifySubmittedChanges() {
+ return notifySubmittedChanges;
+ }
+
+ public void setNotifySubmittedChanges(final boolean a) {
+ notifySubmittedChanges = a;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatchAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatchAccess.java
new file mode 100644
index 0000000000..ce444895b6
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountProjectWatchAccess.java
@@ -0,0 +1,42 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountProjectWatchAccess extends
+ Access<AccountProjectWatch, AccountProjectWatch.Key> {
+ @PrimaryKey("key")
+ AccountProjectWatch get(AccountProjectWatch.Key key) throws OrmException;
+
+ @Query("WHERE key.accountId = ?")
+ ResultSet<AccountProjectWatch> byAccount(Account.Id id) throws OrmException;
+
+ @Query("WHERE notifyNewChanges = true AND key.projectName = ?")
+ ResultSet<AccountProjectWatch> notifyNewChanges(Project.NameKey name)
+ throws OrmException;
+
+ @Query("WHERE notifyAllComments = true AND key.projectName = ?")
+ ResultSet<AccountProjectWatch> notifyAllComments(Project.NameKey name)
+ throws OrmException;
+
+ @Query("WHERE notifySubmittedChanges = true AND key.projectName = ?")
+ ResultSet<AccountProjectWatch> notifySubmittedChanges(Project.NameKey name)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKey.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKey.java
new file mode 100644
index 0000000000..dd98695f04
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKey.java
@@ -0,0 +1,153 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+
+import java.sql.Timestamp;
+
+/** An SSH key approved for use by an {@link Account}. */
+public final class AccountSshKey {
+ public static class Id extends IntKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected int seq;
+
+ protected Id() {
+ accountId = new Account.Id();
+ }
+
+ public Id(final Account.Id a, final int s) {
+ accountId = a;
+ seq = s;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ @Override
+ public int get() {
+ return seq;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ seq = newValue;
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected AccountSshKey.Id id;
+
+ @Column(length = Integer.MAX_VALUE)
+ protected String sshPublicKey;
+
+ @Column
+ protected Timestamp storedOn;
+
+ @Column
+ protected boolean valid;
+
+ @Column(notNull = false)
+ protected Timestamp lastUsedOn;
+
+ protected AccountSshKey() {
+ }
+
+ public AccountSshKey(final AccountSshKey.Id i, final String pub) {
+ id = i;
+ sshPublicKey = pub;
+ storedOn = new Timestamp(System.currentTimeMillis());
+ valid = true; // We can assume it is fine.
+ }
+
+ public Account.Id getAccount() {
+ return id.accountId;
+ }
+
+ public AccountSshKey.Id getKey() {
+ return id;
+ }
+
+ public String getSshPublicKey() {
+ return sshPublicKey;
+ }
+
+ public String getAlgorithm() {
+ final String s = getSshPublicKey();
+ if (s == null || s.length() == 0) {
+ return "none";
+ }
+
+ final String[] parts = s.split(" ");
+ if (parts.length < 1) {
+ return "none";
+ }
+ return parts[0];
+ }
+
+ public String getEncodedKey() {
+ final String s = getSshPublicKey();
+ if (s == null || s.length() == 0) {
+ return null;
+ }
+
+ final String[] parts = s.split(" ");
+ if (parts.length < 2) {
+ return null;
+ }
+ return parts[1];
+ }
+
+ public String getComment() {
+ final String s = getSshPublicKey();
+ if (s == null || s.length() == 0) {
+ return "";
+ }
+
+ final String[] parts = s.split(" ", 3);
+ if (parts.length < 3) {
+ return "";
+ }
+ return parts[2];
+ }
+
+ public Timestamp getStoredOn() {
+ return storedOn;
+ }
+
+ public boolean isValid() {
+ return valid;
+ }
+
+ public void setInvalid() {
+ valid = false;
+ }
+
+ public Timestamp getLastUsedOn() {
+ return lastUsedOn;
+ }
+
+ public void setLastUsedOn() {
+ lastUsedOn = new Timestamp(System.currentTimeMillis());
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKeyAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKeyAccess.java
new file mode 100644
index 0000000000..c572dee660
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountSshKeyAccess.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface AccountSshKeyAccess extends
+ Access<AccountSshKey, AccountSshKey.Id> {
+ @PrimaryKey("id")
+ AccountSshKey get(AccountSshKey.Id id) throws OrmException;
+
+ @Query("WHERE id.accountId = ? ORDER BY storedOn DESC")
+ ResultSet<AccountSshKey> byAccount(Account.Id id) throws OrmException;
+
+ @Query("WHERE id.accountId = ? AND valid = true ORDER BY storedOn DESC")
+ ResultSet<AccountSshKey> valid(Account.Id id) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
new file mode 100644
index 0000000000..789c2deb21
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
@@ -0,0 +1,169 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.Key;
+import com.google.gwtorm.client.StringKey;
+
+/** Types of approvals that can be associated with a {@link Change}. */
+public final class ApprovalCategory {
+ /** Id of the special "Submit" action (and category). */
+ public static final ApprovalCategory.Id SUBMIT =
+ new ApprovalCategory.Id("SUBM");
+
+ /** Id of the special "Read" action (and category). */
+ public static final ApprovalCategory.Id READ =
+ new ApprovalCategory.Id("READ");
+
+ /** Id of the special "Own" category; manages a project. */
+ public static final ApprovalCategory.Id OWN = new ApprovalCategory.Id("OWN");
+
+ /** Id of the special "Push Annotated Tag" action (and category). */
+ public static final ApprovalCategory.Id PUSH_TAG =
+ new ApprovalCategory.Id("pTAG");
+ public static final short PUSH_TAG_SIGNED = 1;
+ public static final short PUSH_TAG_ANNOTATED = 2;
+ public static final short PUSH_TAG_ANY = 3;
+
+ /** Id of the special "Push Branch" action (and category). */
+ public static final ApprovalCategory.Id PUSH_HEAD =
+ new ApprovalCategory.Id("pHD");
+ public static final short PUSH_HEAD_UPDATE = 1;
+ public static final short PUSH_HEAD_CREATE = 2;
+ public static final short PUSH_HEAD_REPLACE = 3;
+
+ public static class Id extends StringKey<Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(length = 4)
+ protected String id;
+
+ protected Id() {
+ }
+
+ public Id(final String a) {
+ id = a;
+ }
+
+ @Override
+ public String get() {
+ return id;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ id = newValue;
+ }
+
+ /** True if the right can inherit from the magical "-- All Projects --". */
+ public boolean canInheritFromWildProject() {
+ if (OWN.equals(this)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /** Internal short unique identifier for this category. */
+ @Column
+ protected Id categoryId;
+
+ /** Unique name for this category, shown in the web interface to users. */
+ @Column(length = 20)
+ protected String name;
+
+ /** Abbreviated form of {@link #name} for display in very wide tables. */
+ @Column(length = 4, notNull = false)
+ protected String abbreviatedName;
+
+ /**
+ * Order of this category within the Approvals table when presented.
+ * <p>
+ * If < 0 (e.g. -1) this category is not shown in the Approvals table but is
+ * instead considered to be an action that the user might be able to perform,
+ * e.g. "Submit".
+ * <p>
+ * If >= 0 this category is shown in the Approvals table, sorted along with
+ * its siblings by <code>position, name</code>.
+ */
+ @Column
+ protected short position;
+
+ /** Identity of the function used to aggregate the category's value. */
+ @Column
+ protected String functionName;
+
+ /** If set, the minimum score is copied during patch set replacement. */
+ @Column
+ protected boolean copyMinScore;
+
+ protected ApprovalCategory() {
+ }
+
+ public ApprovalCategory(final ApprovalCategory.Id id, final String name) {
+ this.categoryId = id;
+ this.name = name;
+ this.functionName = "MaxWithBlock";
+ }
+
+ public ApprovalCategory.Id getId() {
+ return categoryId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String n) {
+ name = n;
+ }
+
+ public String getAbbreviatedName() {
+ return abbreviatedName;
+ }
+
+ public void setAbbreviatedName(final String n) {
+ abbreviatedName = n;
+ }
+
+ public short getPosition() {
+ return position;
+ }
+
+ public void setPosition(final short p) {
+ position = p;
+ }
+
+ public boolean isAction() {
+ return position < 0;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public void setFunctionName(final String name) {
+ functionName = name;
+ }
+
+ public boolean isCopyMinScore() {
+ return copyMinScore;
+ }
+
+ public void setCopyMinScore(final boolean copy) {
+ copyMinScore = copy;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryAccess.java
new file mode 100644
index 0000000000..80bc9952dc
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryAccess.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.SecondaryKey;
+
+public interface ApprovalCategoryAccess extends
+ Access<ApprovalCategory, ApprovalCategory.Id> {
+ @PrimaryKey("categoryId")
+ ApprovalCategory get(ApprovalCategory.Id id) throws OrmException;
+
+ @SecondaryKey("name")
+ ApprovalCategory byName(String name) throws OrmException;
+
+ @Query("ORDER BY position, name")
+ ResultSet<ApprovalCategory> all() throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValue.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValue.java
new file mode 100644
index 0000000000..c91dacb459
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValue.java
@@ -0,0 +1,108 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.ShortKey;
+
+/** Valid value for a {@link ApprovalCategory}. */
+public final class ApprovalCategoryValue {
+ public static class Id extends ShortKey<ApprovalCategory.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected ApprovalCategory.Id categoryId;
+
+ @Column
+ protected short value;
+
+ protected Id() {
+ categoryId = new ApprovalCategory.Id();
+ }
+
+ public Id(final ApprovalCategory.Id cat, final short v) {
+ categoryId = cat;
+ value = v;
+ }
+
+ @Override
+ public ApprovalCategory.Id getParentKey() {
+ return categoryId;
+ }
+
+ @Override
+ public short get() {
+ return value;
+ }
+
+ @Override
+ protected void set(short newValue) {
+ value = newValue;
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Id key;
+
+ @Column(length = 50)
+ protected String name;
+
+ protected ApprovalCategoryValue() {
+ }
+
+ public ApprovalCategoryValue(final ApprovalCategoryValue.Id id,
+ final String name) {
+ this.key = id;
+ this.name = name;
+ }
+
+ public ApprovalCategoryValue.Id getId() {
+ return key;
+ }
+
+ public ApprovalCategory.Id getCategoryId() {
+ return key.categoryId;
+ }
+
+ public short getValue() {
+ return key.value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String n) {
+ name = n;
+ }
+
+ public String formatValue() {
+ if (getValue() < 0) {
+ return Short.toString(getValue());
+ } else if (getValue() == 0) {
+ return " 0";
+ } else {
+ return "+" + Short.toString(getValue());
+ }
+ }
+
+ public String format() {
+ final StringBuilder m = new StringBuilder();
+ m.append(formatValue());
+ m.append(' ');
+ m.append(getName());
+ return m.toString();
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValueAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValueAccess.java
new file mode 100644
index 0000000000..e86d652bea
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategoryValueAccess.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface ApprovalCategoryValueAccess extends
+ Access<ApprovalCategoryValue, ApprovalCategoryValue.Id> {
+ @PrimaryKey("key")
+ ApprovalCategoryValue get(ApprovalCategoryValue.Id key) throws OrmException;
+
+ @Query("WHERE key.categoryId = ? ORDER BY key.value")
+ ResultSet<ApprovalCategoryValue> byCategory(ApprovalCategory.Id id)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AuthType.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AuthType.java
new file mode 100644
index 0000000000..8dabf88789
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AuthType.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+public enum AuthType {
+ /** Login relies upon the OpenID standard: {@link "http://openid.net/"} */
+ OPENID,
+
+ /**
+ * Login relies upon the container/web server security.
+ * <p>
+ * The container or web server must populate an HTTP header with a unique name
+ * for the current user. Gerrit will implicitly trust the value of this header
+ * to supply the unique identity.
+ */
+ HTTP,
+
+ /**
+ * Login relies upon the container/web server security, but also uses LDAP.
+ * <p>
+ * Like {@link #HTTP}, the container or web server must populate an HTTP
+ * header with a unique name for the current user. Gerrit will implicitly
+ * trust the value of this header to supply the unique identity.
+ * <p>
+ * In addition to trusting the HTTP headers, Gerrit will obtain basic user
+ * registration (name and email) from LDAP, and some group memberships.
+ */
+ HTTP_LDAP,
+
+ /**
+ * Login collects username and password through a web form, and binds to LDAP.
+ * <p>
+ * Unlike {@link #HTTP_LDAP}, Gerrit presents a sign-in dialog to the user and
+ * makes the connection to the LDAP server on their behalf.
+ */
+ LDAP,
+
+ /** Development mode to enable becoming anyone you want. */
+ DEVELOPMENT_BECOME_ANY_ACCOUNT;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Branch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Branch.java
new file mode 100644
index 0000000000..0a3b392467
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Branch.java
@@ -0,0 +1,101 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+/** Line of development within a {@link Project}. */
+public final class Branch {
+ public static final String R_HEADS = "refs/heads/";
+ public static final String R_REFS = "refs/";
+
+ /** Branch name key */
+ public static class NameKey extends StringKey<Project.NameKey> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Project.NameKey projectName;
+
+ @Column
+ protected String branchName;
+
+ protected NameKey() {
+ projectName = new Project.NameKey();
+ }
+
+ public NameKey(final Project.NameKey proj, final String n) {
+ projectName = proj;
+ branchName = n;
+ }
+
+ @Override
+ public String get() {
+ return branchName;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ branchName = newValue;
+ }
+
+ @Override
+ public Project.NameKey getParentKey() {
+ return projectName;
+ }
+
+ public String getShortName() {
+ final String n = get();
+
+ // Git style branches will tend to start with "refs/heads/".
+ //
+ if (n.startsWith(R_HEADS)) {
+ return n.substring(R_HEADS.length());
+ }
+
+ return n;
+ }
+ }
+
+ protected NameKey name;
+ protected RevId revision;
+
+ protected Branch() {
+ }
+
+ public Branch(final Branch.NameKey newName) {
+ name = newName;
+ }
+
+ public Branch.NameKey getNameKey() {
+ return name;
+ }
+
+ public String getName() {
+ return name.get();
+ }
+
+ public String getShortName() {
+ return name.getShortName();
+ }
+
+ public RevId getRevision() {
+ return revision;
+ }
+
+ public void setRevision(final RevId id) {
+ revision = id;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Change.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Change.java
new file mode 100644
index 0000000000..86104ebfe4
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Change.java
@@ -0,0 +1,451 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+import com.google.gwtorm.client.RowVersion;
+import com.google.gwtorm.client.StringKey;
+
+import java.sql.Timestamp;
+
+/**
+ * A change proposed to be merged into a {@link Branch}.
+ * <p>
+ * The data graph rooted below a Change can be quite complex:
+ *
+ * <pre>
+ * {@link Change}
+ * |
+ * +- {@link ChangeMessage}: &quot;cover letter&quot; or general comment.
+ * |
+ * +- {@link PatchSet}: a single variant of this change.
+ * |
+ * +- {@link PatchSetApproval}: a +/- vote on the change's current state.
+ * |
+ * +- {@link PatchSetAncestor}: parents of this change's commit.
+ * |
+ * +- {@link PatchLineComment}: comment about a specific line
+ * </pre>
+ * <p>
+ * <h5>PatchSets</h5>
+ * <p>
+ * Every change has at least one PatchSet. A change starts out with one
+ * PatchSet, the initial proposal put forth by the change owner. This
+ * {@link Account} is usually also listed as the author and committer in the
+ * PatchSetInfo.
+ * <p>
+ * The {@link PatchSetAncestor} entities are a mirror of the Git commit
+ * metadata, providing access to the information without needing direct
+ * accessing Git. These entities are actually legacy artifacts from Gerrit 1.x
+ * and could be removed, replaced by direct RevCommit access.
+ * <p>
+ * Each PatchSet contains zero or more Patch records, detailing the file paths
+ * impacted by the change (otherwise known as, the file paths the author
+ * added/deleted/modified). Sometimes a merge commit can contain zero patches,
+ * if the merge has no conflicts, or has no impact other than to cut off a line
+ * of development.
+ * <p>
+ * Each PatchLineComment is a draft or a published comment about a single line
+ * of the associated file. These are the inline comment entities created by
+ * users as they perform a review.
+ * <p>
+ * When additional PatchSets appear under a change, these PatchSets reference
+ * <i>replacement</i> commits; alternative commits that could be made to the
+ * project instead of the original commit referenced by the first PatchSet.
+ * <p>
+ * A change has at most one current PatchSet. The current PatchSet is updated
+ * when a new replacement PatchSet is uploaded. When a change is submitted, the
+ * current patch set is what is merged into the destination branch.
+ * <p>
+ * <h5>ChangeMessage</h5>
+ * <p>
+ * The ChangeMessage entity is a general free-form comment about the whole
+ * change, rather than PatchLineComment's file and line specific context. The
+ * ChangeMessage appears at the start of any email generated by Gerrit, and is
+ * shown on the change overview page, rather than in a file-specific context.
+ * Users often use this entity to describe general remarks about the overall
+ * concept proposed by the change.
+ * <p>
+ * <h5>PatchSetApproval</h5>
+ * <p>
+ * PatchSetApproval entities exist to fill in the <i>cells</i> of the approvals
+ * table in the web UI. That is, a single PatchSetApproval record's key is the
+ * tuple {@code (PatchSet,Account,ApprovalCategory)}. Each PatchSetApproval
+ * carries with it a small score value, typically within the range -2..+2.
+ * <p>
+ * If an Account has created only PatchSetApprovals with a score value of 0, the
+ * Change shows in their dashboard, and they are said to be CC'd (carbon copied)
+ * on the Change, but are not a direct reviewer. This often happens when an
+ * account was specified at upload time with the {@code --cc} command line flag,
+ * or have published comments, but left the approval scores at 0 ("No Score").
+ * <p>
+ * If an Account has one or more PatchSetApprovals with a score != 0, the Change
+ * shows in their dashboard, and they are said to be an active reviewer. Such
+ * individuals are highlighted when notice of a replacement patch set is sent,
+ * or when notice of the change submission occurs.
+ */
+public final class Change {
+ public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected int id;
+
+ protected Id() {
+ }
+
+ public Id(final int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ id = newValue;
+ }
+
+ /** Parse a Change.Id out of a string representation. */
+ public static Id parse(final String str) {
+ final Id r = new Id();
+ r.fromString(str);
+ return r;
+ }
+ }
+
+ /** Globally unique identification of this change. */
+ public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(length = 60)
+ protected String id;
+
+ protected Key() {
+ }
+
+ public Key(final String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String get() {
+ return id;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ id = newValue;
+ }
+
+ /** Construct a key that is after all keys prefixed by this key. */
+ public Key max() {
+ final StringBuilder revEnd = new StringBuilder(get().length() + 1);
+ revEnd.append(get());
+ revEnd.append('\u9fa5');
+ return new Key(revEnd.toString());
+ }
+
+ /** Obtain a shorter version of this key string, using a leading prefix. */
+ public String abbreviate() {
+ final String s = get();
+ return s.substring(0, Math.min(s.length(), 9));
+ }
+
+ /** Parse a Change.Key out of a string representation. */
+ public static Key parse(final String str) {
+ final Key r = new Key();
+ r.fromString(str);
+ return r;
+ }
+ }
+
+ /** Minimum database status constant for an open change. */
+ private static final char MIN_OPEN = 'a';
+ /** Database constant for {@link Status#NEW}. */
+ protected static final char STATUS_NEW = 'n';
+ /** Database constant for {@link Status#SUBMITTED}. */
+ protected static final char STATUS_SUBMITTED = 's';
+ /** Maximum database status constant for an open change. */
+ private static final char MAX_OPEN = 'z';
+
+ /** Database constant for {@link Status#MERGED}. */
+ protected static final char STATUS_MERGED = 'M';
+
+ /**
+ * Current state within the basic workflow of the change.
+ *
+ * <p>
+ * Within the database, lower case codes ('a'..'z') indicate a change that is
+ * still open, and that can be modified/refined further, while upper case
+ * codes ('A'..'Z') indicate a change that is closed and cannot be further
+ * modified.
+ * */
+ public static enum Status {
+ /**
+ * Change is open and pending review, or review is in progress.
+ *
+ * <p>
+ * This is the default state assigned to a change when it is first created
+ * in the database. A change stays in the NEW state throughout its review
+ * cycle, until the change is submitted or abandoned.
+ *
+ * <p>
+ * Changes in the NEW state can be moved to:
+ * <ul>
+ * <li>{@link #SUBMITTED} - when the Submit Patch Set action is used;
+ * <li>{@link #ABANDONED} - when the Abandon action is used.
+ * </ul>
+ */
+ NEW(STATUS_NEW),
+
+ /**
+ * Change is open, but has been submitted to the merge queue.
+ *
+ * <p>
+ * A change enters the SUBMITTED state when an authorized user presses the
+ * "submit" action through the web UI, requesting that Gerrit merge the
+ * change's current patch set into the destination branch.
+ *
+ * <p>
+ * Typically a change resides in the SUBMITTED for only a brief sub-second
+ * period while the merge queue fires and the destination branch is updated.
+ * However, if a dependency commit (a {@link PatchSetAncestor}, directly or
+ * transitively) is not yet merged into the branch, the change will hang in
+ * the SUBMITTED state indefinately.
+ *
+ * <p>
+ * Changes in the SUBMITTED state can be moved to:
+ * <ul>
+ * <li>{@link #NEW} - when a replacement patch set is supplied, OR when a
+ * merge conflict is detected;
+ * <li>{@link #MERGED} - when the change has been successfully merged into
+ * the destination branch;
+ * <li>{@link #ABANDONED} - when the Abandon action is used.
+ * </ul>
+ */
+ SUBMITTED(STATUS_SUBMITTED),
+
+ /**
+ * Change is closed, and submitted to its destination branch.
+ *
+ * <p>
+ * Once a change has been merged, it cannot be further modified by adding a
+ * replacement patch set. Draft comments however may be published,
+ * supporting a post-submit review.
+ */
+ MERGED(STATUS_MERGED),
+
+ /**
+ * Change is closed, but was not submitted to its destination branch.
+ *
+ * <p>
+ * Once a change has been abandoned, it cannot be further modified by adding
+ * a replacement patch set, and it cannot be merged. Draft comments however
+ * may be published, permitting reviewers to send constructive feedback.
+ */
+ ABANDONED('A');
+
+ private final char code;
+ private final boolean closed;
+
+ private Status(final char c) {
+ code = c;
+ closed = !(MIN_OPEN <= c && c <= MAX_OPEN);
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+
+ public static Status forCode(final char c) {
+ for (final Status s : Status.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ /** Locally assigned unique identifier of the change */
+ @Column
+ protected Id changeId;
+
+ /** Globally assigned unique identifier of the change */
+ @Column
+ protected Key changeKey;
+
+ /** optimistic locking */
+ @Column
+ @RowVersion
+ protected int rowVersion;
+
+ /** When this change was first introduced into the database. */
+ @Column
+ protected Timestamp createdOn;
+
+ /**
+ * When was a meaningful modification last made to this record's data
+ * <p>
+ * Note, this update timestamp includes its children.
+ */
+ @Column
+ protected Timestamp lastUpdatedOn;
+
+ /** A {@link #lastUpdatedOn} ASC,{@link #changeId} ASC for sorting. */
+ @Column(length = 16)
+ protected String sortKey;
+
+ @Column(name = "owner_account_id")
+ protected Account.Id owner;
+
+ /** The branch (and project) this change merges into. */
+ @Column
+ protected Branch.NameKey dest;
+
+ /** Is the change currently open? Set to {@link #status}.isOpen(). */
+ @Column
+ protected boolean open;
+
+ /** Current state code; see {@link Status}. */
+ @Column
+ protected char status;
+
+ /** The total number of {@link PatchSet} children in this Change. */
+ @Column
+ protected int nbrPatchSets;
+
+ /** The current patch set. */
+ @Column
+ protected int currentPatchSetId;
+
+ /** Subject from the current patch set. */
+ @Column
+ protected String subject;
+
+ protected Change() {
+ }
+
+ public Change(final Change.Key newKey, final Change.Id newId,
+ final Account.Id ownedBy, final Branch.NameKey forBranch) {
+ changeKey = newKey;
+ changeId = newId;
+ createdOn = new Timestamp(System.currentTimeMillis());
+ lastUpdatedOn = createdOn;
+ owner = ownedBy;
+ dest = forBranch;
+ setStatus(Status.NEW);
+ }
+
+ /** Legacy 32 bit integer identity for a change. */
+ @Deprecated
+ public Change.Id getId() {
+ return changeId;
+ }
+
+ /** Legacy 32 bit integer identity for a change. */
+ @Deprecated
+ public int getChangeId() {
+ return changeId.get();
+ }
+
+ /** The Change-Id tag out of the initial commit, or a natural key. */
+ public Change.Key getKey() {
+ return changeKey;
+ }
+
+ public void setKey(final Change.Key k) {
+ changeKey = k;
+ }
+
+ public Timestamp getCreatedOn() {
+ return createdOn;
+ }
+
+ public Timestamp getLastUpdatedOn() {
+ return lastUpdatedOn;
+ }
+
+ public void resetLastUpdatedOn() {
+ lastUpdatedOn = new Timestamp(System.currentTimeMillis());
+ }
+
+ public String getSortKey() {
+ return sortKey;
+ }
+
+ public void setSortKey(final String newSortKey) {
+ sortKey = newSortKey;
+ }
+
+ public Account.Id getOwner() {
+ return owner;
+ }
+
+ public Branch.NameKey getDest() {
+ return dest;
+ }
+
+ public Project.NameKey getProject() {
+ return dest.getParentKey();
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ /** Get the id of the most current {@link PatchSet} in this change. */
+ public PatchSet.Id currentPatchSetId() {
+ if (currentPatchSetId > 0) {
+ return new PatchSet.Id(changeId, currentPatchSetId);
+ }
+ return null;
+ }
+
+ public void setCurrentPatchSet(final PatchSetInfo ps) {
+ currentPatchSetId = ps.getKey().get();
+ subject = ps.getSubject();
+ }
+
+ /**
+ * Allocate a new PatchSet id within this change.
+ * <p>
+ * <b>Note: This makes the change dirty. Call update() after.</b>
+ */
+ public PatchSet.Id newPatchSetId() {
+ return new PatchSet.Id(changeId, ++nbrPatchSets);
+ }
+
+ public Status getStatus() {
+ return Status.forCode(status);
+ }
+
+ public void setStatus(final Status newStatus) {
+ open = newStatus.isOpen();
+ status = newStatus.getCode();
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeAccess.java
new file mode 100644
index 0000000000..b95204bbb2
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeAccess.java
@@ -0,0 +1,90 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface ChangeAccess extends Access<Change, Change.Id> {
+ @PrimaryKey("changeId")
+ Change get(Change.Id id) throws OrmException;
+
+ @Query("WHERE changeKey = ?")
+ ResultSet<Change> byKey(Change.Key key) throws OrmException;
+
+ @Query("WHERE changeKey >= ? AND changeKey <= ?")
+ ResultSet<Change> byKeyRange(Change.Key reva, Change.Key revb)
+ throws OrmException;
+
+ @Query("WHERE dest.projectName = ? AND changeKey = ?")
+ ResultSet<Change> byProjectKey(Project.NameKey p, Change.Key key)
+ throws OrmException;
+
+ @Query("WHERE owner = ? AND open = true ORDER BY createdOn, changeId")
+ ResultSet<Change> byOwnerOpen(Account.Id id) throws OrmException;
+
+ @Query("WHERE owner = ? AND open = false ORDER BY lastUpdatedOn DESC LIMIT 5")
+ ResultSet<Change> byOwnerClosed(Account.Id id) throws OrmException;
+
+ @Query("WHERE owner = ? AND open = false ORDER BY lastUpdatedOn")
+ ResultSet<Change> byOwnerClosedAll(Account.Id id) throws OrmException;
+
+ @Query("WHERE dest = ? AND status = '" + Change.STATUS_SUBMITTED
+ + "' ORDER BY lastUpdatedOn")
+ ResultSet<Change> submitted(Branch.NameKey dest) throws OrmException;
+
+ @Query("WHERE status = '" + Change.STATUS_SUBMITTED + "'")
+ ResultSet<Change> allSubmitted() throws OrmException;
+
+ @Query("WHERE open = true AND sortKey > ? ORDER BY sortKey LIMIT ?")
+ ResultSet<Change> allOpenPrev(String sortKey, int limit) throws OrmException;
+
+ @Query("WHERE open = true AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
+ ResultSet<Change> allOpenNext(String sortKey, int limit) throws OrmException;
+
+ @Query("WHERE open = true AND dest.projectName = ?")
+ ResultSet<Change> byProjectOpenAll(Project.NameKey p) throws OrmException;
+
+ @Query("WHERE open = true AND dest.projectName = ? AND sortKey > ?"
+ + " ORDER BY sortKey LIMIT ?")
+ ResultSet<Change> byProjectOpenPrev(Project.NameKey p, String sortKey,
+ int limit) throws OrmException;
+
+ @Query("WHERE open = true AND dest.projectName = ? AND sortKey < ?"
+ + " ORDER BY sortKey DESC LIMIT ?")
+ ResultSet<Change> byProjectOpenNext(Project.NameKey p, String sortKey,
+ int limit) throws OrmException;
+
+ @Query("WHERE open = false AND status = ? AND dest.projectName = ? AND sortKey > ?"
+ + " ORDER BY sortKey LIMIT ?")
+ ResultSet<Change> byProjectClosedPrev(char status, Project.NameKey p,
+ String sortKey, int limit) throws OrmException;
+
+ @Query("WHERE open = false AND status = ? AND dest.projectName = ? AND sortKey < ?"
+ + " ORDER BY sortKey DESC LIMIT ?")
+ ResultSet<Change> byProjectClosedNext(char status, Project.NameKey p,
+ String sortKey, int limit) throws OrmException;
+
+ @Query("WHERE open = false AND status = ? AND sortKey > ? ORDER BY sortKey LIMIT ?")
+ ResultSet<Change> allClosedPrev(char status, String sortKey, int limit)
+ throws OrmException;
+
+ @Query("WHERE open = false AND status = ? AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
+ ResultSet<Change> allClosedNext(char status, String sortKey, int limit)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessage.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessage.java
new file mode 100644
index 0000000000..5b27aedb6e
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessage.java
@@ -0,0 +1,114 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+import java.sql.Timestamp;
+
+/** A message attached to a {@link Change}. */
+public final class ChangeMessage {
+ public static class Key extends StringKey<Change.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Change.Id changeId;
+
+ @Column(length = 40)
+ protected String uuid;
+
+ protected Key() {
+ changeId = new Change.Id();
+ }
+
+ public Key(final Change.Id change, final String uuid) {
+ this.changeId = change;
+ this.uuid = uuid;
+ }
+
+ @Override
+ public Change.Id getParentKey() {
+ return changeId;
+ }
+
+ @Override
+ public String get() {
+ return uuid;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ uuid = newValue;
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ /** Who wrote this comment; null if it was written by the Gerrit system. */
+ @Column(name = "author_id", notNull = false)
+ protected Account.Id author;
+
+ /** When this comment was drafted. */
+ @Column
+ protected Timestamp writtenOn;
+
+ /** The text left by the user. */
+ @Column(notNull = false, length = Integer.MAX_VALUE)
+ protected String message;
+
+ protected ChangeMessage() {
+ }
+
+ public ChangeMessage(final ChangeMessage.Key k, final Account.Id a) {
+ this(k, a, new Timestamp(System.currentTimeMillis()));
+ }
+
+ public ChangeMessage(final ChangeMessage.Key k, final Account.Id a,
+ final Timestamp wo) {
+ key = k;
+ author = a;
+ writtenOn = wo;
+ }
+
+ public ChangeMessage.Key getKey() {
+ return key;
+ }
+
+ /** If null, the message was written 'by the Gerrit system'. */
+ public Account.Id getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(final Account.Id accountId) {
+ if (author != null) {
+ throw new IllegalStateException("Cannot modify author once assigned");
+ }
+ author = accountId;
+ }
+
+ public Timestamp getWrittenOn() {
+ return writtenOn;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(final String s) {
+ message = s;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessageAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessageAccess.java
new file mode 100644
index 0000000000..377aa59c79
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ChangeMessageAccess.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface ChangeMessageAccess extends
+ Access<ChangeMessage, ChangeMessage.Key> {
+ @PrimaryKey("key")
+ ChangeMessage get(ChangeMessage.Key id) throws OrmException;
+
+ @Query("WHERE key.changeId = ? ORDER BY writtenOn")
+ ResultSet<ChangeMessage> byChange(Change.Id id) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CodedEnum.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CodedEnum.java
new file mode 100644
index 0000000000..59002929d8
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/CodedEnum.java
@@ -0,0 +1,20 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+/** Extension of Enum which provides distinct character code values. */
+public interface CodedEnum {
+ char getCode();
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContactInformation.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContactInformation.java
new file mode 100644
index 0000000000..b8af7790e7
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContactInformation.java
@@ -0,0 +1,85 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+
+/** Non-Internet contact details, such as a postal address and telephone. */
+public final class ContactInformation {
+ @Column(length = Integer.MAX_VALUE, notNull = false)
+ protected String address;
+
+ @Column(notNull = false, length = 40)
+ protected String country;
+
+ @Column(notNull = false, length = 30)
+ protected String phoneNbr;
+
+ @Column(notNull = false, length = 30)
+ protected String faxNbr;
+
+ public ContactInformation() {
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(final String a) {
+ address = a;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(final String c) {
+ country = c;
+ }
+
+ public String getPhoneNumber() {
+ return phoneNbr;
+ }
+
+ public void setPhoneNumber(final String p) {
+ phoneNbr = p;
+ }
+
+ public String getFaxNumber() {
+ return faxNbr;
+ }
+
+ public void setFaxNumber(final String f) {
+ faxNbr = f;
+ }
+
+ public static boolean hasData(final ContactInformation contactInformation) {
+ if (contactInformation == null) {
+ return false;
+ }
+ return hasData(contactInformation.address)
+ || hasData(contactInformation.country)
+ || hasData(contactInformation.phoneNbr)
+ || hasData(contactInformation.faxNbr);
+ }
+
+ public static boolean hasAddress(final ContactInformation contactInformation) {
+ return contactInformation != null && hasData(contactInformation.address);
+ }
+
+ private static boolean hasData(final String s) {
+ return s != null && s.trim().length() > 0;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreement.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreement.java
new file mode 100644
index 0000000000..ba22ffbbc1
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreement.java
@@ -0,0 +1,139 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+
+/**
+ * An agreement {@link Account} must acknowledge to contribute changes.
+ *
+ * @see AccountAgreement
+ */
+public final class ContributorAgreement {
+ public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(name = "cla_id")
+ protected int id;
+
+ protected Id() {
+ }
+
+ public Id(final int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ id = newValue;
+ }
+ }
+
+ @Column
+ protected Id id;
+
+ /** Is this an active agreement contributors can use. */
+ @Column
+ protected boolean active;
+
+ /** Does this agreement require the {@link Account} to have contact details? */
+ @Column
+ protected boolean requireContactInformation;
+
+ /** Does this agreement automatically verify new accounts? */
+ @Column
+ protected boolean autoVerify;
+
+ /** A short name for the agreement. */
+ @Column(length = 40)
+ protected String shortName;
+
+ /** A short one-line description text to appear next to the name. */
+ @Column(notNull = false)
+ protected String shortDescription;
+
+ /** Web address of the agreement documentation. */
+ @Column
+ protected String agreementUrl;
+
+ protected ContributorAgreement() {
+ }
+
+ /**
+ * Create a new agreement.
+ *
+ * @param newId unique id, see {@link ReviewDb#nextAccountId()}.
+ * @param name a short title/name for the agreement.
+ */
+ public ContributorAgreement(final ContributorAgreement.Id newId,
+ final String name) {
+ id = newId;
+ shortName = name;
+ }
+
+ public ContributorAgreement.Id getId() {
+ return id;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(final boolean a) {
+ active = a;
+ }
+
+ public boolean isAutoVerify() {
+ return autoVerify;
+ }
+
+ public void setAutoVerify(final boolean g) {
+ autoVerify = g;
+ }
+
+ public boolean isRequireContactInformation() {
+ return requireContactInformation;
+ }
+
+ public void setRequireContactInformation(final boolean r) {
+ requireContactInformation = r;
+ }
+
+ public String getShortName() {
+ return shortName;
+ }
+
+ public String getShortDescription() {
+ return shortDescription;
+ }
+
+ public void setShortDescription(final String d) {
+ shortDescription = d;
+ }
+
+ public String getAgreementUrl() {
+ return agreementUrl;
+ }
+
+ public void setAgreementUrl(final String h) {
+ agreementUrl = h;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreementAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreementAccess.java
new file mode 100644
index 0000000000..ae7b41d557
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ContributorAgreementAccess.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+/** Access interface for {@link ContributorAgreement}. */
+public interface ContributorAgreementAccess extends
+ Access<ContributorAgreement, ContributorAgreement.Id> {
+ @PrimaryKey("id")
+ ContributorAgreement get(ContributorAgreement.Id key) throws OrmException;
+
+ @Query("WHERE active = true ORDER BY shortName")
+ ResultSet<ContributorAgreement> active() throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java
new file mode 100644
index 0000000000..8f05379a7a
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Patch.java
@@ -0,0 +1,272 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+/** A single modified file in a {@link PatchSet}. */
+public final class Patch {
+ public static class Key extends StringKey<PatchSet.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(name = Column.NONE)
+ protected PatchSet.Id patchSetId;
+
+ @Column
+ protected String fileName;
+
+ protected Key() {
+ patchSetId = new PatchSet.Id();
+ }
+
+ public Key(final PatchSet.Id ps, final String name) {
+ this.patchSetId = ps;
+ this.fileName = name;
+ }
+
+ @Override
+ public PatchSet.Id getParentKey() {
+ return patchSetId;
+ }
+
+ @Override
+ public String get() {
+ return fileName;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ fileName = newValue;
+ }
+
+ /** Parse a Patch.Id out of a string representation. */
+ public static Key parse(final String str) {
+ final Key r = new Key();
+ r.fromString(str);
+ return r;
+ }
+
+ public String getFileName() {
+ return get();
+ }
+ }
+
+ /** Type of modification made to the file path. */
+ public static enum ChangeType implements CodedEnum {
+ /** Path is being created/introduced by this patch. */
+ ADDED('A'),
+
+ /** Path already exists, and has updated content. */
+ MODIFIED('M'),
+
+ /** Path existed, but is being removed by this patch. */
+ DELETED('D'),
+
+ /** Path existed at {@link Patch#getSourceFileName()} but was moved. */
+ RENAMED('R'),
+
+ /** Path was copied from {@link Patch#getSourceFileName()}. */
+ COPIED('C');
+
+ private final char code;
+
+ private ChangeType(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static ChangeType forCode(final char c) {
+ for (final ChangeType s : ChangeType.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ /** Type of formatting for this patch. */
+ public static enum PatchType implements CodedEnum {
+ /**
+ * A textual difference between two versions.
+ *
+ * <p>
+ * A UNIFIED patch can be rendered in multiple ways. Most commonly, it is
+ * rendered as a side by side display using two columns, left column for the
+ * old version, right column for the new version. A UNIFIED patch can also
+ * be formatted in a number of standard "patch script" styles, but typically
+ * is formatted in the POSIX standard unified diff format.
+ *
+ * <p>
+ * Usually Gerrit renders a UNIFIED patch in a PatchScreen.SideBySide view,
+ * presenting the file in two columns. If the user chooses, a
+ * PatchScreen.Unified is also a valid display method.
+ * */
+ UNIFIED('U'),
+
+ /**
+ * Difference of two (or more) binary contents.
+ *
+ * <p>
+ * A BINARY patch cannot be viewed in a text display, as it represents a
+ * change in binary content at the associated path, for example, an image
+ * file has been replaced with a different image.
+ *
+ * <p>
+ * Gerrit can only render a BINARY file in a PatchScreen.Unified view, as
+ * the only information it can display is the old and new file content
+ * hashes.
+ */
+ BINARY('B'),
+
+ /**
+ * Difference of three or more textual contents.
+ *
+ * <p>
+ * Git can produce an n-way unified diff, showing how a merge conflict was
+ * resolved when two or more conflicting branches were merged together in a
+ * single merge commit.
+ *
+ * <p>
+ * This type of patch can only appear if there are two or more
+ * {@link PatchSetAncestor} entities for the same parent {@link PatchSet},
+ * as that denotes that the patch set is a merge commit.
+ *
+ * <p>
+ * Gerrit can only render an N_WAY file in a PatchScreen.Unified view, as it
+ * does not have code to split the n-way unified diff into multiple edit
+ * lists, one per pre-image. However, a logical way to display this format
+ * would be an n-way table, with n+1 columns displayed (n pre-images, +1
+ * post-image).
+ */
+ N_WAY('N');
+
+ private final char code;
+
+ private PatchType(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static PatchType forCode(final char c) {
+ for (final PatchType s : PatchType.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ protected Key key;
+
+ /** What sort of change is this to the path; see {@link ChangeType}. */
+ protected char changeType;
+
+ /** What type of patch is this; see {@link PatchType}. */
+ protected char patchType;
+
+ /** Number of published comments on this patch. */
+ protected int nbrComments;
+
+ /** Number of drafts by the current user; not persisted in the datastore. */
+ protected int nbrDrafts;
+
+ /**
+ * Original if {@link #changeType} is {@link ChangeType#COPIED} or
+ * {@link ChangeType#RENAMED}.
+ */
+ protected String sourceFileName;
+
+ /** True if this patch has been reviewed by the current logged in user */
+ private boolean reviewedByCurrentUser;
+
+ protected Patch() {
+ }
+
+ public Patch(final Patch.Key newId) {
+ key = newId;
+ setChangeType(ChangeType.MODIFIED);
+ setPatchType(PatchType.UNIFIED);
+ }
+
+ public Patch.Key getKey() {
+ return key;
+ }
+
+ public int getCommentCount() {
+ return nbrComments;
+ }
+
+ public void setCommentCount(final int n) {
+ nbrComments = n;
+ }
+
+ public int getDraftCount() {
+ return nbrDrafts;
+ }
+
+ public void setDraftCount(final int n) {
+ nbrDrafts = n;
+ }
+
+ public ChangeType getChangeType() {
+ return ChangeType.forCode(changeType);
+ }
+
+ public void setChangeType(final ChangeType type) {
+ changeType = type.getCode();
+ }
+
+ public PatchType getPatchType() {
+ return PatchType.forCode(patchType);
+ }
+
+ public void setPatchType(final PatchType type) {
+ patchType = type.getCode();
+ }
+
+ public String getFileName() {
+ return key.fileName;
+ }
+
+ public String getSourceFileName() {
+ return sourceFileName;
+ }
+
+ public void setSourceFileName(final String n) {
+ sourceFileName = n;
+ }
+
+ public boolean isReviewedByCurrentUser() {
+ return reviewedByCurrentUser;
+ }
+
+ public void setReviewedByCurrentUser(boolean r) {
+ reviewedByCurrentUser = r;
+ }
+
+ @Override
+ public String toString() {
+ return "[Patch " + getKey().toString() + "]";
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineComment.java
new file mode 100644
index 0000000000..a9c419c86c
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineComment.java
@@ -0,0 +1,177 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+import java.sql.Timestamp;
+
+/** A comment left by a user on a specific line of a {@link Patch}. */
+public final class PatchLineComment {
+ public static class Key extends StringKey<Patch.Key> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(name = Column.NONE)
+ protected Patch.Key patchKey;
+
+ @Column(length = 40)
+ protected String uuid;
+
+ protected Key() {
+ patchKey = new Patch.Key();
+ }
+
+ public Key(final Patch.Key p, final String uuid) {
+ this.patchKey = p;
+ this.uuid = uuid;
+ }
+
+ @Override
+ public Patch.Key getParentKey() {
+ return patchKey;
+ }
+
+ @Override
+ public String get() {
+ return uuid;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ uuid = newValue;
+ }
+ }
+
+ protected static final char STATUS_DRAFT = 'd';
+ protected static final char STATUS_PUBLISHED = 'P';
+
+ public static enum Status {
+ DRAFT(STATUS_DRAFT),
+
+ PUBLISHED(STATUS_PUBLISHED);
+
+ private final char code;
+
+ private Status(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static Status forCode(final char c) {
+ for (final Status s : Status.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ /** Line number this comment applies to; it should display after the line. */
+ @Column
+ protected int lineNbr;
+
+ /** Who wrote this comment. */
+ @Column(name = "author_id")
+ protected Account.Id author;
+
+ /** When this comment was drafted. */
+ @Column
+ protected Timestamp writtenOn;
+
+ /** Current publication state of the comment; see {@link Status}. */
+ @Column
+ protected char status;
+
+ /** Which file is this comment; 0 is ancestor, 1 is new version. */
+ @Column
+ protected short side;
+
+ /** The text left by the user. */
+ @Column(notNull = false, length = Integer.MAX_VALUE)
+ protected String message;
+
+ /** The parent of this comment, or null if this is the first comment on this line */
+ @Column(length = 40, notNull = false)
+ protected String parentUuid;
+
+ protected PatchLineComment() {
+ }
+
+ public PatchLineComment(final PatchLineComment.Key id, final int line,
+ final Account.Id a, String parentUuid) {
+ key = id;
+ lineNbr = line;
+ author = a;
+ this.parentUuid = parentUuid;
+ setStatus(Status.DRAFT);
+ updated();
+ }
+
+ public PatchLineComment.Key getKey() {
+ return key;
+ }
+
+ public int getLine() {
+ return lineNbr;
+ }
+
+ public Account.Id getAuthor() {
+ return author;
+ }
+
+ public Timestamp getWrittenOn() {
+ return writtenOn;
+ }
+
+ public Status getStatus() {
+ return Status.forCode(status);
+ }
+
+ public void setStatus(final Status s) {
+ status = s.getCode();
+ }
+
+ public short getSide() {
+ return side;
+ }
+
+ public void setSide(final short s) {
+ side = s;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(final String s) {
+ message = s;
+ }
+
+ public void updated() {
+ writtenOn = new Timestamp(System.currentTimeMillis());
+ }
+
+ public String getParentUuid() {
+ return parentUuid;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineCommentAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineCommentAccess.java
new file mode 100644
index 0000000000..6beacde7eb
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchLineCommentAccess.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface PatchLineCommentAccess extends
+ Access<PatchLineComment, PatchLineComment.Key> {
+ @PrimaryKey("key")
+ PatchLineComment get(PatchLineComment.Key id) throws OrmException;
+
+ @Query("WHERE key.patchKey = ? AND status = '"
+ + PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
+ ResultSet<PatchLineComment> published(Patch.Key patch) throws OrmException;
+
+ @Query("WHERE key.patchKey.patchSetId.changeId = ?"
+ + " AND key.patchKey.fileName = ? AND status = '"
+ + PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
+ ResultSet<PatchLineComment> published(Change.Id id, String file)
+ throws OrmException;
+
+ @Query("WHERE key.patchKey.patchSetId = ? AND status = '"
+ + PatchLineComment.STATUS_PUBLISHED + "'")
+ ResultSet<PatchLineComment> published(PatchSet.Id patchset)
+ throws OrmException;
+
+ @Query("WHERE key.patchKey.patchSetId = ? AND status = '"
+ + PatchLineComment.STATUS_DRAFT
+ + "' AND author = ? ORDER BY key.patchKey,lineNbr,writtenOn")
+ ResultSet<PatchLineComment> draft(PatchSet.Id patchset, Account.Id author)
+ throws OrmException;
+
+ @Query("WHERE key.patchKey = ? AND status = '"
+ + PatchLineComment.STATUS_DRAFT
+ + "' AND author = ? ORDER BY lineNbr,writtenOn")
+ ResultSet<PatchLineComment> draft(Patch.Key patch, Account.Id author)
+ throws OrmException;
+
+ @Query("WHERE key.patchKey.patchSetId.changeId = ?"
+ + " AND key.patchKey.fileName = ? AND author = ? AND status = '"
+ + PatchLineComment.STATUS_DRAFT + "' ORDER BY lineNbr,writtenOn")
+ ResultSet<PatchLineComment> draft(Change.Id id, String file, Account.Id author)
+ throws OrmException;
+
+ @Query("WHERE status = '" + PatchLineComment.STATUS_DRAFT
+ + "' AND author = ?")
+ ResultSet<PatchLineComment> draftByAuthor(Account.Id author)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSet.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSet.java
new file mode 100644
index 0000000000..771981c7a0
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSet.java
@@ -0,0 +1,159 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+
+import java.sql.Timestamp;
+
+/** A single revision of a {@link Change}. */
+public final class PatchSet {
+ private static final String REFS_CHANGES = "refs/changes/";
+
+ /** Is the reference name a change reference? */
+ public static boolean isRef(final String name) {
+ return name.matches("^refs/changes/.*/[1-9][0-9]*/[1-9][0-9]*$");
+ }
+
+ public static class Id extends IntKey<Change.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Change.Id changeId;
+
+ @Column
+ protected int patchSetId;
+
+ protected Id() {
+ changeId = new Change.Id();
+ }
+
+ public Id(final Change.Id change, final int id) {
+ this.changeId = change;
+ this.patchSetId = id;
+ }
+
+ @Override
+ public Change.Id getParentKey() {
+ return changeId;
+ }
+
+ @Override
+ public int get() {
+ return patchSetId;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ patchSetId = newValue;
+ }
+
+ /** Parse a PatchSet.Id out of a string representation. */
+ public static Id parse(final String str) {
+ final Id r = new Id();
+ r.fromString(str);
+ return r;
+ }
+
+ /** Parse a PatchSet.Id from a {@link PatchSet#getRefName()} result. */
+ public static Id fromRef(String name) {
+ if (!name.startsWith(REFS_CHANGES)) {
+ throw new IllegalArgumentException("Not a PatchSet.Id: " + name);
+ }
+ final String[] parts = name.substring(REFS_CHANGES.length()).split("/");
+ final int n = parts.length;
+ if (n < 2) {
+ throw new IllegalArgumentException("Not a PatchSet.Id: " + name);
+ }
+ final int changeId = Integer.parseInt(parts[n - 2]);
+ final int patchSetId = Integer.parseInt(parts[n - 1]);
+ return new PatchSet.Id(new Change.Id(changeId), patchSetId);
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Id id;
+
+ @Column(notNull = false)
+ protected RevId revision;
+
+ @Column(name = "uploader_account_id")
+ protected Account.Id uploader;
+
+ /** When this patch set was first introduced onto the change. */
+ @Column
+ protected Timestamp createdOn;
+
+ protected PatchSet() {
+ }
+
+ public PatchSet(final PatchSet.Id k) {
+ id = k;
+ }
+
+ public PatchSet.Id getId() {
+ return id;
+ }
+
+ public int getPatchSetId() {
+ return id.get();
+ }
+
+ public RevId getRevision() {
+ return revision;
+ }
+
+ public void setRevision(final RevId i) {
+ revision = i;
+ }
+
+ public Account.Id getUploader() {
+ return uploader;
+ }
+
+ public void setUploader(final Account.Id who) {
+ uploader = who;
+ }
+
+ public Timestamp getCreatedOn() {
+ return createdOn;
+ }
+
+ public void setCreatedOn(final Timestamp ts) {
+ createdOn = ts;
+ }
+
+ public String getRefName() {
+ final StringBuilder r = new StringBuilder();
+ r.append(REFS_CHANGES);
+ final int changeId = id.getParentKey().get();
+ final int m = changeId % 100;
+ if (m < 10) {
+ r.append('0');
+ }
+ r.append(m);
+ r.append('/');
+ r.append(changeId);
+ r.append('/');
+ r.append(id.get());
+ return r.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "[PatchSet " + getId().toString() + "]";
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAccess.java
new file mode 100644
index 0000000000..fa594f7694
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAccess.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface PatchSetAccess extends Access<PatchSet, PatchSet.Id> {
+ @PrimaryKey("id")
+ PatchSet get(PatchSet.Id id) throws OrmException;
+
+ @Query("WHERE id.changeId = ? ORDER BY id.patchSetId")
+ ResultSet<PatchSet> byChange(Change.Id id) throws OrmException;
+
+ @Query("WHERE id.changeId = ? AND revision = ?")
+ ResultSet<PatchSet> byChangeRevision(Change.Id id, RevId rev)
+ throws OrmException;
+
+ @Query("WHERE revision = ? LIMIT 2")
+ ResultSet<PatchSet> byRevision(RevId rev) throws OrmException;
+
+ @Query("WHERE revision >= ? AND revision <= ? LIMIT 2")
+ ResultSet<PatchSet> byRevisionRange(RevId reva, RevId revb)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestor.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestor.java
new file mode 100644
index 0000000000..f22d3f79d2
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestor.java
@@ -0,0 +1,88 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+
+/** Ancestors of a {@link PatchSet} that the PatchSet depends upon. */
+public final class PatchSetAncestor {
+ public static class Id extends IntKey<PatchSet.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(name = Column.NONE)
+ protected PatchSet.Id patchSetId;
+
+ @Column
+ protected int position;
+
+ protected Id() {
+ patchSetId = new PatchSet.Id();
+ }
+
+ public Id(final PatchSet.Id psId, final int pos) {
+ this.patchSetId = psId;
+ this.position = pos;
+ }
+
+ @Override
+ public PatchSet.Id getParentKey() {
+ return patchSetId;
+ }
+
+ @Override
+ public int get() {
+ return position;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ position = newValue;
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Id key;
+
+ @Column
+ protected RevId ancestorRevision;
+
+ protected PatchSetAncestor() {
+ }
+
+ public PatchSetAncestor(final PatchSetAncestor.Id k) {
+ key = k;
+ }
+
+ public PatchSetAncestor.Id getId() {
+ return key;
+ }
+
+ public PatchSet.Id getPatchSet() {
+ return key.patchSetId;
+ }
+
+ public int getPosition() {
+ return key.position;
+ }
+
+ public RevId getAncestorRevision() {
+ return ancestorRevision;
+ }
+
+ public void setAncestorRevision(final RevId id) {
+ ancestorRevision = id;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestorAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestorAccess.java
new file mode 100644
index 0000000000..eeea37218d
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetAncestorAccess.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface PatchSetAncestorAccess extends
+ Access<PatchSetAncestor, PatchSetAncestor.Id> {
+ @PrimaryKey("key")
+ PatchSetAncestor get(PatchSetAncestor.Id key) throws OrmException;
+
+ @Query("WHERE key.patchSetId = ? ORDER BY key.position")
+ ResultSet<PatchSetAncestor> ancestorsOf(PatchSet.Id id) throws OrmException;
+
+ @Query("WHERE ancestorRevision = ?")
+ ResultSet<PatchSetAncestor> descendantsOf(RevId revision)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApproval.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApproval.java
new file mode 100644
index 0000000000..b3a58df483
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApproval.java
@@ -0,0 +1,145 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+import java.sql.Timestamp;
+
+/** An approval (or negative approval) on a patch set. */
+public final class PatchSetApproval {
+ public static class Key extends CompoundKey<PatchSet.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column(name = Column.NONE)
+ protected PatchSet.Id patchSetId;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected ApprovalCategory.Id categoryId;
+
+ protected Key() {
+ patchSetId = new PatchSet.Id();
+ accountId = new Account.Id();
+ categoryId = new ApprovalCategory.Id();
+ }
+
+ public Key(final PatchSet.Id ps, final Account.Id a,
+ final ApprovalCategory.Id c) {
+ this.patchSetId = ps;
+ this.accountId = a;
+ this.categoryId = c;
+ }
+
+ @Override
+ public PatchSet.Id getParentKey() {
+ return patchSetId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {accountId, categoryId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ /**
+ * Value assigned by the user.
+ * <p>
+ * The precise meaning of "value" is up to each category.
+ * <p>
+ * In general:
+ * <ul>
+ * <li><b>&lt; 0:</b> The approval is rejected/revoked.</li>
+ * <li><b>= 0:</b> No indication either way is provided.</li>
+ * <li><b>&gt; 0:</b> The approval is approved/positive.</li>
+ * </ul>
+ * and in the negative and positive direction a magnitude can be assumed.The
+ * further from 0 the more assertive the approval.
+ */
+ @Column
+ protected short value;
+
+ @Column
+ protected Timestamp granted;
+
+ /** <i>Cached copy of Change.open.</i> */
+ @Column
+ protected boolean changeOpen;
+
+ /** <i>Cached copy of Change.sortKey</i>; only if {@link #changeOpen} = false */
+ @Column(length = 16, notNull = false)
+ protected String changeSortKey;
+
+ protected PatchSetApproval() {
+ }
+
+ public PatchSetApproval(final PatchSetApproval.Key k, final short v) {
+ key = k;
+ changeOpen = true;
+ setValue(v);
+ setGranted();
+ }
+
+ public PatchSetApproval(final PatchSet.Id psId, final PatchSetApproval src) {
+ key =
+ new PatchSetApproval.Key(psId, src.getAccountId(), src.getCategoryId());
+ changeOpen = true;
+ value = src.getValue();
+ granted = src.granted;
+ }
+
+ public PatchSetApproval.Key getKey() {
+ return key;
+ }
+
+ public PatchSet.Id getPatchSetId() {
+ return key.patchSetId;
+ }
+
+ public Account.Id getAccountId() {
+ return key.accountId;
+ }
+
+ public ApprovalCategory.Id getCategoryId() {
+ return key.categoryId;
+ }
+
+ public short getValue() {
+ return value;
+ }
+
+ public void setValue(final short v) {
+ value = v;
+ }
+
+ public Timestamp getGranted() {
+ return granted;
+ }
+
+ public void setGranted() {
+ granted = new Timestamp(System.currentTimeMillis());
+ }
+
+ public void cache(final Change c) {
+ changeOpen = c.open;
+ changeSortKey = c.sortKey;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApprovalAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApprovalAccess.java
new file mode 100644
index 0000000000..417d264ccf
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetApprovalAccess.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface PatchSetApprovalAccess extends
+ Access<PatchSetApproval, PatchSetApproval.Key> {
+ @PrimaryKey("key")
+ PatchSetApproval get(PatchSetApproval.Key key) throws OrmException;
+
+ @Query("WHERE key.patchSetId.changeId = ?")
+ ResultSet<PatchSetApproval> byChange(Change.Id id) throws OrmException;
+
+ @Query("WHERE key.patchSetId = ?")
+ ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) throws OrmException;
+
+ @Query("WHERE key.patchSetId = ? AND key.accountId = ?")
+ ResultSet<PatchSetApproval> byPatchSetUser(PatchSet.Id patchSet,
+ Account.Id account) throws OrmException;
+
+ @Query("WHERE changeOpen = true AND key.accountId = ?")
+ ResultSet<PatchSetApproval> openByUser(Account.Id account)
+ throws OrmException;
+
+ @Query("WHERE changeOpen = false AND key.accountId = ?"
+ + " ORDER BY changeSortKey DESC LIMIT 10")
+ ResultSet<PatchSetApproval> closedByUser(Account.Id account)
+ throws OrmException;
+
+ @Query("WHERE changeOpen = false AND key.accountId = ? ORDER BY changeSortKey")
+ ResultSet<PatchSetApproval> closedByUserAll(Account.Id account)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetInfo.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetInfo.java
new file mode 100644
index 0000000000..39fd8fa5d3
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/PatchSetInfo.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+
+/**
+ * Additional data about a {@link PatchSet} not normally loaded.
+ */
+public final class PatchSetInfo {
+ protected PatchSet.Id key;
+
+ /** First line of {@link #message}. */
+ protected String subject;
+
+ /** The complete description of the change the patch set introduces. */
+ protected String message;
+
+ /** Identity of who wrote the patch set. May differ from {@link #committer}. */
+ protected UserIdentity author;
+
+ /** Identity of who committed the patch set to the VCS. */
+ protected UserIdentity committer;
+
+ protected PatchSetInfo() {
+ }
+
+ public PatchSetInfo(final PatchSet.Id k) {
+ key = k;
+ }
+
+ public PatchSet.Id getKey() {
+ return key;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(final String s) {
+ if (s != null && s.length() > 255) {
+ subject = s.substring(0, 255);
+ } else {
+ subject = s;
+ }
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(final String m) {
+ message = m;
+ }
+
+ public UserIdentity getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(final UserIdentity u) {
+ author = u;
+ }
+
+ public UserIdentity getCommitter() {
+ return committer;
+ }
+
+ public void setCommitter(final UserIdentity u) {
+ committer = u;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
new file mode 100644
index 0000000000..ae79919bdb
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
@@ -0,0 +1,188 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.IntKey;
+import com.google.gwtorm.client.StringKey;
+
+/** Projects match a source code repository managed by Gerrit */
+public final class Project {
+ /** Project name key */
+ public static class NameKey extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected String name;
+
+ protected NameKey() {
+ }
+
+ public NameKey(final String n) {
+ name = n;
+ }
+
+ @Override
+ public String get() {
+ return name;
+ }
+
+ @Override
+ protected void set(String newValue) {
+ name = newValue;
+ }
+
+ /** Parse a Project.NameKey out of a string representation. */
+ public static NameKey parse(final String str) {
+ final NameKey r = new NameKey();
+ r.fromString(str);
+ return r;
+ }
+ }
+
+ /** Synthetic key to link to within the database */
+ public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected int id;
+
+ protected Id() {
+ }
+
+ public Id(final int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int get() {
+ return id;
+ }
+
+ @Override
+ protected void set(int newValue) {
+ id = newValue;
+ }
+ }
+
+ public static enum SubmitType {
+ FAST_FORWARD_ONLY('F'),
+
+ MERGE_IF_NECESSARY('M'),
+
+ MERGE_ALWAYS('A'),
+
+ CHERRY_PICK('C');
+
+ private final char code;
+
+ private SubmitType(final char c) {
+ code = c;
+ }
+
+ public char getCode() {
+ return code;
+ }
+
+ public static SubmitType forCode(final char c) {
+ for (final SubmitType s : SubmitType.values()) {
+ if (s.code == c) {
+ return s;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Column
+ protected NameKey name;
+
+ @Column
+ protected Id projectId;
+
+ @Column(length = Integer.MAX_VALUE, notNull = false)
+ protected String description;
+
+ @Column
+ protected boolean useContributorAgreements;
+
+ @Column
+ protected boolean useSignedOffBy;
+
+ @Column
+ protected char submitType;
+
+ protected Project() {
+ }
+
+ public Project(final Project.NameKey newName, final Project.Id newId) {
+ name = newName;
+ projectId = newId;
+ useContributorAgreements = true;
+ setSubmitType(SubmitType.MERGE_IF_NECESSARY);
+ }
+
+ public Project.Id getId() {
+ return projectId;
+ }
+
+ public Project.NameKey getNameKey() {
+ return name;
+ }
+
+ public String getName() {
+ return name.get();
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(final String d) {
+ description = d;
+ }
+
+ public boolean isUseContributorAgreements() {
+ return useContributorAgreements;
+ }
+
+ public void setUseContributorAgreements(final boolean u) {
+ useContributorAgreements = u;
+ }
+
+ public boolean isUseSignedOffBy() {
+ return useSignedOffBy;
+ }
+
+ public void setUseSignedOffBy(final boolean sbo) {
+ useSignedOffBy = sbo;
+ }
+
+ public SubmitType getSubmitType() {
+ return SubmitType.forCode(submitType);
+ }
+
+ public void setSubmitType(final SubmitType type) {
+ submitType = type.getCode();
+ }
+
+ public void copySettingsFrom(final Project update) {
+ description = update.description;
+ useContributorAgreements = update.useContributorAgreements;
+ useSignedOffBy = update.useSignedOffBy;
+ submitType = update.submitType;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
new file mode 100644
index 0000000000..e406a8fdec
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.SecondaryKey;
+
+public interface ProjectAccess extends Access<Project, Project.NameKey> {
+ @PrimaryKey("name")
+ Project get(Project.NameKey name) throws OrmException;
+
+ @SecondaryKey("projectId")
+ Project get(Project.Id id) throws OrmException;
+
+ @Query("ORDER BY name")
+ ResultSet<Project> all() throws OrmException;
+
+ @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
+ ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
+ throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRight.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRight.java
new file mode 100644
index 0000000000..7dddc28f71
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRight.java
@@ -0,0 +1,109 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+/** Grant to use an {@link ApprovalCategory} in the scope of a {@link Project}. */
+public final class ProjectRight {
+ public static class Key extends CompoundKey<Project.NameKey> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Project.NameKey projectName;
+
+ @Column
+ protected ApprovalCategory.Id categoryId;
+
+ @Column
+ protected AccountGroup.Id groupId;
+
+ protected Key() {
+ projectName = new Project.NameKey();
+ categoryId = new ApprovalCategory.Id();
+ groupId = new AccountGroup.Id();
+ }
+
+ public Key(final Project.NameKey p, final ApprovalCategory.Id a,
+ final AccountGroup.Id g) {
+ projectName = p;
+ categoryId = a;
+ groupId = g;
+ }
+
+ @Override
+ public Project.NameKey getParentKey() {
+ return projectName;
+ }
+
+ public Project.NameKey getProjectNameKey() {
+ return projectName;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {categoryId, groupId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ @Column
+ protected short minValue;
+
+ @Column
+ protected short maxValue;
+
+ protected ProjectRight() {
+ }
+
+ public ProjectRight(final ProjectRight.Key k) {
+ key = k;
+ }
+
+ public ProjectRight.Key getKey() {
+ return key;
+ }
+
+ public Project.NameKey getProjectNameKey() {
+ return key.projectName;
+ }
+
+ public ApprovalCategory.Id getApprovalCategoryId() {
+ return key.categoryId;
+ }
+
+ public AccountGroup.Id getAccountGroupId() {
+ return key.groupId;
+ }
+
+ public short getMinValue() {
+ return minValue;
+ }
+
+ public void setMinValue(final short m) {
+ minValue = m;
+ }
+
+ public short getMaxValue() {
+ return maxValue;
+ }
+
+ public void setMaxValue(final short m) {
+ maxValue = m;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRightAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRightAccess.java
new file mode 100644
index 0000000000..49ccda72a0
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectRightAccess.java
@@ -0,0 +1,34 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface ProjectRightAccess extends
+ Access<ProjectRight, ProjectRight.Key> {
+ @PrimaryKey("key")
+ ProjectRight get(ProjectRight.Key key) throws OrmException;
+
+ @Query("WHERE key.projectName = ?")
+ ResultSet<ProjectRight> byProject(Project.NameKey name) throws OrmException;
+
+ @Query("WHERE key.categoryId = ? AND key.groupId = ?")
+ ResultSet<ProjectRight> byCategoryGroup(ApprovalCategory.Id cat,
+ AccountGroup.Id group) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RevId.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RevId.java
new file mode 100644
index 0000000000..bea6fb8aad
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RevId.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+
+/** A revision identifier for a file or a change. */
+public final class RevId {
+ public static final int LEN = 40;
+
+ @Column(length = LEN)
+ protected String id;
+
+ protected RevId() {
+ }
+
+ public RevId(final String str) {
+ id = str;
+ }
+
+ /** @return the value of this revision id. */
+ public String get() {
+ return id;
+ }
+
+ /** @return true if this revision id has all required digits. */
+ public boolean isComplete() {
+ return get().length() == LEN;
+ }
+
+ /**
+ * @return if {@link #isComplete()}, <code>this</code>; otherwise a new RevId
+ * with 'z' appended on the end.
+ */
+ public RevId max() {
+ if (isComplete()) {
+ return this;
+ }
+
+ final StringBuilder revEnd = new StringBuilder(get().length() + 1);
+ revEnd.append(get());
+ revEnd.append('z');
+ return new RevId(revEnd.toString());
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
new file mode 100644
index 0000000000..f962e33cca
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
@@ -0,0 +1,135 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Relation;
+import com.google.gwtorm.client.Schema;
+import com.google.gwtorm.client.Sequence;
+
+/**
+ * The review service database schema.
+ * <p>
+ * Root entities that are at the top level of some important data graph:
+ * <ul>
+ * <li>{@link Project}: Configuration for a single Git repository.</li>
+ * <li>{@link Account}: Per-user account registration, preferences, identity.</li>
+ * <li>{@link Change}: All review information about a single proposed change.</li>
+ * <li>{@link SystemConfig}: Server-wide settings, managed by administrator.</li>
+ * </ul>
+ */
+public interface ReviewDb extends Schema {
+ public static final int VERSION = 19;
+
+ @Relation
+ SchemaVersionAccess schemaVersion();
+
+ @Relation
+ SystemConfigAccess systemConfig();
+
+ @Relation
+ ApprovalCategoryAccess approvalCategories();
+
+ @Relation
+ ApprovalCategoryValueAccess approvalCategoryValues();
+
+ @Relation
+ ContributorAgreementAccess contributorAgreements();
+
+ @Relation
+ AccountAccess accounts();
+
+ @Relation
+ AccountExternalIdAccess accountExternalIds();
+
+ @Relation
+ AccountSshKeyAccess accountSshKeys();
+
+ @Relation
+ AccountAgreementAccess accountAgreements();
+
+ @Relation
+ AccountGroupAccess accountGroups();
+
+ @Relation
+ AccountGroupMemberAccess accountGroupMembers();
+
+ @Relation
+ AccountGroupMemberAuditAccess accountGroupMembersAudit();
+
+ @Relation
+ AccountGroupAgreementAccess accountGroupAgreements();
+
+ @Relation
+ StarredChangeAccess starredChanges();
+
+ @Relation
+ AccountProjectWatchAccess accountProjectWatches();
+
+ @Relation
+ AccountPatchReviewAccess accountPatchReviews();
+
+ @Relation
+ ProjectAccess projects();
+
+ @Relation
+ ProjectRightAccess projectRights();
+
+ @Relation
+ ChangeAccess changes();
+
+ @Relation
+ PatchSetApprovalAccess patchSetApprovals();
+
+ @Relation
+ ChangeMessageAccess changeMessages();
+
+ @Relation
+ PatchSetAccess patchSets();
+
+ @Relation
+ PatchSetAncestorAccess patchSetAncestors();
+
+ @Relation
+ PatchLineCommentAccess patchComments();
+
+ /** Create the next unique id for an {@link Account}. */
+ @Sequence(startWith = 1000000)
+ int nextAccountId() throws OrmException;
+
+ /** Create the next unique id for a {@link ContributorAgreement}. */
+ @Sequence
+ int nextContributorAgreementId() throws OrmException;
+
+ /** Next unique id for a {@link AccountGroup}. */
+ @Sequence
+ int nextAccountGroupId() throws OrmException;
+
+ /** Next unique id for a {@link Project}. */
+ @Sequence
+ int nextProjectId() throws OrmException;
+
+ /** Next unique id for a {@link Change}. */
+ @Sequence
+ int nextChangeId() throws OrmException;
+
+ /**
+ * Next id for a block of {@link ChangeMessage} records.
+ *
+ * @see com.google.gerrit.server.ChangeUtil#messageUUID(ReviewDb)
+ */
+ @Sequence
+ int nextChangeMessageId() throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java
new file mode 100644
index 0000000000..896d827e25
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersion.java
@@ -0,0 +1,61 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+/** Current version of the database schema, to facilitate live upgrades. */
+public final class SchemaVersion {
+ public static final class Key extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ private static final String VALUE = "X";
+
+ @Column(length = 1)
+ protected String one = VALUE;
+
+ public Key() {
+ }
+
+ @Override
+ public String get() {
+ return VALUE;
+ }
+
+ @Override
+ protected void set(final String newValue) {
+ assert get().equals(newValue);
+ }
+ }
+
+ /** Construct a new, unconfigured instance. */
+ public static SchemaVersion create() {
+ final SchemaVersion r = new SchemaVersion();
+ r.singleton = new SchemaVersion.Key();
+ return r;
+ }
+
+ @Column
+ protected Key singleton;
+
+ /** Current version number of the schema. */
+ @Column
+ public transient int versionNbr;
+
+ protected SchemaVersion() {
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java
new file mode 100644
index 0000000000..520435c961
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SchemaVersionAccess.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+
+/** Access interface for {@link SchemaVersion}. */
+public interface SchemaVersionAccess extends
+ Access<SchemaVersion, SchemaVersion.Key> {
+ @PrimaryKey("singleton")
+ SchemaVersion get(SchemaVersion.Key key) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChange.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChange.java
new file mode 100644
index 0000000000..2f6307930a
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChange.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.CompoundKey;
+
+/** A {@link Change} starred by an {@link Account}. */
+public class StarredChange {
+ public static class Key extends CompoundKey<Account.Id> {
+ private static final long serialVersionUID = 1L;
+
+ @Column
+ protected Account.Id accountId;
+
+ @Column
+ protected Change.Id changeId;
+
+ protected Key() {
+ accountId = new Account.Id();
+ changeId = new Change.Id();
+ }
+
+ public Key(final Account.Id a, final Change.Id g) {
+ accountId = a;
+ changeId = g;
+ }
+
+ @Override
+ public Account.Id getParentKey() {
+ return accountId;
+ }
+
+ @Override
+ public com.google.gwtorm.client.Key<?>[] members() {
+ return new com.google.gwtorm.client.Key<?>[] {changeId};
+ }
+ }
+
+ @Column(name = Column.NONE)
+ protected Key key;
+
+ protected StarredChange() {
+ }
+
+ public StarredChange(final StarredChange.Key k) {
+ key = k;
+ }
+
+ public Account.Id getAccountId() {
+ return key.accountId;
+ }
+
+ public Change.Id getChangeId() {
+ return key.changeId;
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChangeAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChangeAccess.java
new file mode 100644
index 0000000000..9e31dbfe85
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/StarredChangeAccess.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+public interface StarredChangeAccess extends
+ Access<StarredChange, StarredChange.Key> {
+ @PrimaryKey("key")
+ StarredChange get(StarredChange.Key key) throws OrmException;
+
+ @Query("WHERE key.accountId = ?")
+ ResultSet<StarredChange> byAccount(Account.Id id) throws OrmException;
+
+ @Query("WHERE key.changeId = ?")
+ ResultSet<StarredChange> byChange(Change.Id id) throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
new file mode 100644
index 0000000000..3238bb05ac
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Column;
+import com.google.gwtorm.client.StringKey;
+
+/** Global configuration needed to serve web requests. */
+public final class SystemConfig {
+ public static final class Key extends
+ StringKey<com.google.gwtorm.client.Key<?>> {
+ private static final long serialVersionUID = 1L;
+
+ private static final String VALUE = "X";
+
+ @Column(length = 1)
+ protected String one = VALUE;
+
+ public Key() {
+ }
+
+ @Override
+ public String get() {
+ return VALUE;
+ }
+
+ @Override
+ protected void set(final String newValue) {
+ assert get().equals(newValue);
+ }
+ }
+
+ /** Construct a new, unconfigured instance. */
+ public static SystemConfig create() {
+ final SystemConfig r = new SystemConfig();
+ r.singleton = new SystemConfig.Key();
+ return r;
+ }
+
+ @Column
+ protected Key singleton;
+
+ /** Private key to sign account identification cookies. */
+ @Column(length = 36)
+ public transient String registerEmailPrivateKey;
+
+ /**
+ * Local filesystem location of header/footer/CSS configuration files
+ */
+ @Column(notNull = false)
+ public transient String sitePath;
+
+ /** Identity of the administration group; those with full access. */
+ @Column
+ public AccountGroup.Id adminGroupId;
+
+ /** Identity of the anonymous group, which permits anyone. */
+ @Column
+ public AccountGroup.Id anonymousGroupId;
+
+ /** Identity of the registered users group, which permits anyone. */
+ @Column
+ public AccountGroup.Id registeredGroupId;
+
+ protected SystemConfig() {
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfigAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfigAccess.java
new file mode 100644
index 0000000000..dfca1caebb
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfigAccess.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import com.google.gwtorm.client.Access;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.PrimaryKey;
+import com.google.gwtorm.client.Query;
+import com.google.gwtorm.client.ResultSet;
+
+/** Access interface for {@link SystemConfig}. */
+public interface SystemConfigAccess extends
+ Access<SystemConfig, SystemConfig.Key> {
+ @PrimaryKey("singleton")
+ SystemConfig get(SystemConfig.Key key) throws OrmException;
+
+ @Query
+ ResultSet<SystemConfig> all() throws OrmException;
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/UserIdentity.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/UserIdentity.java
new file mode 100644
index 0000000000..6eabbda385
--- /dev/null
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/UserIdentity.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.reviewdb;
+
+import java.sql.Timestamp;
+
+public final class UserIdentity {
+ /** Full name of the user. */
+ protected String name;
+
+ /** Email address (or user@host style string anyway). */
+ protected String email;
+
+ /** Time (in UTC) when the identity was constructed. */
+ protected Timestamp when;
+
+ /** Offset from UTC */
+ protected int tz;
+
+ /** If the user has a Gerrit account, their account identity. */
+ protected Account.Id accountId;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String n) {
+ name = n;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(final String e) {
+ email = e;
+ }
+
+ public Timestamp getDate() {
+ return when;
+ }
+
+ public void setDate(final Timestamp d) {
+ when = d;
+ }
+
+ public int getTimeZone() {
+ return tz;
+ }
+
+ public void setTimeZone(final int offset) {
+ tz = offset;
+ }
+
+ public Account.Id getAccount() {
+ return accountId;
+ }
+
+ public void setAccount(final Account.Id id) {
+ accountId = id;
+ }
+}
diff --git a/gerrit-server/.gitignore b/gerrit-server/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-server/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-server/.settings/org.eclipse.core.resources.prefs b/gerrit-server/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-server/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-server/.settings/org.eclipse.core.runtime.prefs b/gerrit-server/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-server/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-server/.settings/org.eclipse.jdt.core.prefs b/gerrit-server/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-server/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-server/.settings/org.eclipse.jdt.ui.prefs b/gerrit-server/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-server/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-server/pom.xml b/gerrit-server/pom.xml
new file mode 100644
index 0000000000..8a5b4e0f25
--- /dev/null
+++ b/gerrit-server/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-server</artifactId>
+ <name>Gerrit Code Review - Server</name>
+
+ <description>
+ Commons server routines
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcpg-jdk15</artifactId>
+ <version>140</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>eu.medsea.mimeutil</groupId>
+ <artifactId>mime-util</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice-servlet</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice-assistedinject</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-ssl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-commonsnet</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr3-maven-plugin</artifactId>
+ <version>3.1.1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>antlr</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/src/main/antlr/com/google/gerrit/server/query/Query.g b/gerrit-server/src/main/antlr/com/google/gerrit/server/query/Query.g
index d17a9de2f2..d17a9de2f2 100644
--- a/src/main/antlr/com/google/gerrit/server/query/Query.g
+++ b/gerrit-server/src/main/antlr/com/google/gerrit/server/query/Query.g
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/Version.java b/gerrit-server/src/main/java/com/google/gerrit/common/Version.java
new file mode 100644
index 0000000000..b4c5e7b872
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/Version.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+public class Version {
+ private static final String version;
+
+ public static String getVersion() {
+ return version;
+ }
+
+ static {
+ version = loadVersion();
+ }
+
+ private static String loadVersion() {
+ InputStream in = Version.class.getResourceAsStream("Version.properties");
+ if (in == null) {
+ return null;
+ }
+ try {
+ final Properties p = new Properties();
+ try {
+ p.load(in);
+ } finally {
+ in.close();
+ }
+ return p.getProperty("version");
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private Version() {
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/AccessPath.java b/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java
index 8188824904..8188824904 100644
--- a/src/main/java/com/google/gerrit/server/AccessPath.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/AccessPath.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
new file mode 100644
index 0000000000..a17149743f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.util.Collections;
+import java.util.Set;
+
+/** An anonymous user who has not yet authenticated. */
+@Singleton
+public class AnonymousUser extends CurrentUser {
+ @Inject
+ AnonymousUser(final AuthConfig auth) {
+ super(AccessPath.UNKNOWN, auth);
+ }
+
+ @Override
+ public Set<AccountGroup.Id> getEffectiveGroups() {
+ return authConfig.getAnonymousGroups();
+ }
+
+ @Override
+ public Set<Change.Id> getStarredChanges() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String toString() {
+ return "ANONYMOUS";
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
new file mode 100644
index 0000000000..b7decd74da
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+
+import org.eclipse.jgit.util.Base64;
+import org.eclipse.jgit.util.NB;
+
+public class ChangeUtil {
+ private static int uuidPrefix;
+ private static int uuidSeq;
+
+ /**
+ * Generate a new unique identifier for change message entities.
+ *
+ * @param db the database connection, used to increment the change message
+ * allocation sequence.
+ * @return the new unique identifier.
+ * @throws OrmException the database couldn't be incremented.
+ */
+ public static String messageUUID(final ReviewDb db) throws OrmException {
+ final byte[] raw = new byte[8];
+ fill(raw, db);
+ return Base64.encodeBytes(raw);
+ }
+
+ private static synchronized void fill(byte[] raw, ReviewDb db)
+ throws OrmException {
+ if (uuidSeq == 0) {
+ uuidPrefix = db.nextChangeMessageId();
+ uuidSeq = Integer.MAX_VALUE;
+ }
+ NB.encodeInt32(raw, 0, uuidPrefix);
+ NB.encodeInt32(raw, 4, uuidSeq--);
+ }
+
+ public static void updated(final Change c) {
+ c.resetLastUpdatedOn();
+ computeSortKey(c);
+ }
+
+ public static void computeSortKey(final Change c) {
+ // The encoding uses minutes since Wed Oct 1 00:00:00 2008 UTC.
+ // We overrun approximately 4,085 years later, so ~6093.
+ //
+ final long lastUpdatedOn =
+ (c.getLastUpdatedOn().getTime() / 1000L) - 1222819200L;
+ final StringBuilder r = new StringBuilder(16);
+ r.setLength(16);
+ formatHexInt(r, 0, (int) (lastUpdatedOn / 60));
+ formatHexInt(r, 8, c.getId().get());
+ c.setSortKey(r.toString());
+ }
+
+ private static final char[] hexchar =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
+ 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ private static void formatHexInt(final StringBuilder dst, final int p, int w) {
+ int o = p + 7;
+ while (o >= p && w != 0) {
+ dst.setCharAt(o--, hexchar[w & 0xf]);
+ w >>>= 4;
+ }
+ while (o >= p) {
+ dst.setCharAt(o--, '0');
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
new file mode 100644
index 0000000000..d3034ceb8a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.servlet.RequestScoped;
+
+import java.util.Set;
+
+/**
+ * Information about the currently logged in user.
+ * <p>
+ * This is a {@link RequestScoped} property managed by Guice.
+ *
+ * @see AnonymousUser
+ * @see IdentifiedUser
+ */
+public abstract class CurrentUser {
+ private final AccessPath accessPath;
+ protected final AuthConfig authConfig;
+
+ protected CurrentUser(final AccessPath accessPath, final AuthConfig authConfig) {
+ this.accessPath = accessPath;
+ this.authConfig = authConfig;
+ }
+
+ /** How this user is accessing the Gerrit Code Review application. */
+ public final AccessPath getAccessPath() {
+ return accessPath;
+ }
+
+ /**
+ * Get the set of groups the user is currently a member of.
+ * <p>
+ * The returned set may be a subset of the user's actual groups; if the user's
+ * account is currently deemed to be untrusted then the effective group set is
+ * only the anonymous and registered user groups. To enable additional groups
+ * (and gain their granted permissions) the user must update their account to
+ * use only trusted authentication providers.
+ *
+ * @return active groups for this user.
+ */
+ public abstract Set<AccountGroup.Id> getEffectiveGroups();
+
+ /** Set of changes starred by this user. */
+ public abstract Set<Change.Id> getStarredChanges();
+
+ @Deprecated
+ public final boolean isAdministrator() {
+ return getEffectiveGroups().contains(authConfig.getAdministratorsGroup());
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/FileTypeRegistry.java b/gerrit-server/src/main/java/com/google/gerrit/server/FileTypeRegistry.java
index 83f87f92ba..83f87f92ba 100644
--- a/src/main/java/com/google/gerrit/server/FileTypeRegistry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/FileTypeRegistry.java
diff --git a/src/main/java/com/google/gerrit/server/GerritPersonIdent.java b/gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdent.java
index 07b49ee922..07b49ee922 100644
--- a/src/main/java/com/google/gerrit/server/GerritPersonIdent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdent.java
diff --git a/src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java
index 2d9bbb91bf..2d9bbb91bf 100644
--- a/src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/GerritPersonIdentProvider.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
new file mode 100644
index 0000000000..b7c2219fcf
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -0,0 +1,238 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.StarredChange;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TimeZone;
+
+/** An authenticated user. */
+public class IdentifiedUser extends CurrentUser {
+ /** Create an IdentifiedUser, ignoring any per-request state. */
+ @Singleton
+ public static class GenericFactory {
+ private final AuthConfig authConfig;
+ private final AccountCache accountCache;
+ private final Realm realm;
+
+ @Inject
+ GenericFactory(final AuthConfig authConfig,
+ final AccountCache accountCache, final Realm realm) {
+ this.authConfig = authConfig;
+ this.accountCache = accountCache;
+ this.realm = realm;
+ }
+
+ public IdentifiedUser create(final Account.Id id) {
+ return new IdentifiedUser(AccessPath.UNKNOWN, authConfig, accountCache,
+ realm, null, null, id);
+ }
+ }
+
+ /**
+ * Create an IdentifiedUser, relying on current request state.
+ * <p>
+ * Can only be used from within a module that has defined request scoped
+ * {@code @RemotePeer SocketAddress} and {@code ReviewDb} providers.
+ */
+ @Singleton
+ public static class RequestFactory {
+ private final AuthConfig authConfig;
+ private final AccountCache accountCache;
+ private final Realm realm;
+ private final Provider<SocketAddress> remotePeerProvider;
+ private final Provider<ReviewDb> dbProvider;
+
+ @Inject
+ RequestFactory(final AuthConfig authConfig,
+ final AccountCache accountCache, final Realm realm,
+ final @RemotePeer Provider<SocketAddress> remotePeerProvider,
+ final Provider<ReviewDb> dbProvider) {
+ this.authConfig = authConfig;
+ this.accountCache = accountCache;
+ this.realm = realm;
+ this.remotePeerProvider = remotePeerProvider;
+ this.dbProvider = dbProvider;
+ }
+
+ public IdentifiedUser create(final AccessPath accessPath,
+ final Account.Id id) {
+ return new IdentifiedUser(accessPath, authConfig, accountCache, realm,
+ remotePeerProvider, dbProvider, id);
+ }
+ }
+
+ private static final Logger log =
+ LoggerFactory.getLogger(IdentifiedUser.class);
+
+ private final Realm realm;
+ private final AccountCache accountCache;
+
+ @Nullable
+ private final Provider<SocketAddress> remotePeerProvider;
+
+ @Nullable
+ private final Provider<ReviewDb> dbProvider;
+
+ private final Account.Id accountId;
+
+ private AccountState state;
+ private Set<String> emailAddresses;
+ private Set<AccountGroup.Id> effectiveGroups;
+ private Set<Change.Id> starredChanges;
+
+ private IdentifiedUser(final AccessPath accessPath,
+ final AuthConfig authConfig, final AccountCache accountCache,
+ final Realm realm,
+ @Nullable final Provider<SocketAddress> remotePeerProvider,
+ @Nullable final Provider<ReviewDb> dbProvider, final Account.Id id) {
+ super(accessPath, authConfig);
+ this.realm = realm;
+ this.accountCache = accountCache;
+ this.remotePeerProvider = remotePeerProvider;
+ this.dbProvider = dbProvider;
+ this.accountId = id;
+ }
+
+ private AccountState state() {
+ if (state == null) {
+ state = accountCache.get(getAccountId());
+ }
+ return state;
+ }
+
+ /** The account identity for the user. */
+ public Account.Id getAccountId() {
+ return accountId;
+ }
+
+ public Account getAccount() {
+ return state().getAccount();
+ }
+
+ public Set<String> getEmailAddresses() {
+ if (emailAddresses == null) {
+ emailAddresses = state().getEmailAddresses();
+ }
+ return emailAddresses;
+ }
+
+ @Override
+ public Set<AccountGroup.Id> getEffectiveGroups() {
+ if (effectiveGroups == null) {
+ if (authConfig.isIdentityTrustable(state().getExternalIds())) {
+ effectiveGroups = realm.groups(state());
+
+ } else {
+ effectiveGroups = authConfig.getRegisteredGroups();
+ }
+ }
+ return effectiveGroups;
+ }
+
+ @Override
+ public Set<Change.Id> getStarredChanges() {
+ if (starredChanges == null) {
+ if (dbProvider == null) {
+ throw new OutOfScopeException("Not in request scoped user");
+ }
+ final Set<Change.Id> h = new HashSet<Change.Id>();
+ try {
+ for (final StarredChange sc : dbProvider.get().starredChanges()
+ .byAccount(getAccountId())) {
+ h.add(sc.getChangeId());
+ }
+ } catch (ProvisionException e) {
+ log.warn("Cannot query starred by user changes", e);
+ } catch (OrmException e) {
+ log.warn("Cannot query starred by user changes", e);
+ }
+ starredChanges = Collections.unmodifiableSet(h);
+ }
+ return starredChanges;
+ }
+
+ public PersonIdent newPersonIdent() {
+ return newPersonIdent(new Date(), TimeZone.getDefault());
+ }
+
+ public PersonIdent newPersonIdent(final Date when, final TimeZone tz) {
+ final Account ua = getAccount();
+ String name = ua.getFullName();
+ if (name == null) {
+ name = ua.getPreferredEmail();
+ }
+ if (name == null) {
+ name = "Anonymous Coward";
+ }
+
+ final String userId = "account-" + ua.getId().toString();
+ final String user;
+ if (ua.getSshUserName() != null) {
+ user = ua.getSshUserName() + "|" + userId;
+ } else {
+ user = userId;
+ }
+
+ String host = null;
+ final SocketAddress remotePeer =
+ remotePeerProvider != null ? remotePeerProvider.get() : null;
+ if (remotePeer instanceof InetSocketAddress) {
+ final InetSocketAddress sa = (InetSocketAddress) remotePeer;
+ final InetAddress in = sa.getAddress();
+ if (in != null) {
+ host = in.getCanonicalHostName();
+ } else {
+ host = sa.getHostName();
+ }
+ }
+ if (host == null) {
+ host = "unknown";
+ }
+
+ return new PersonIdent(name, user + "@" + host, when, tz);
+ }
+
+ @Override
+ public String toString() {
+ return "IdentifiedUser[account " + getAccountId() + "]";
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java b/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
index 7062d95ce0..7062d95ce0 100644
--- a/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/MimeUtilFileTypeRegistry.java
diff --git a/src/main/java/com/google/gerrit/server/ParamertizedString.java b/gerrit-server/src/main/java/com/google/gerrit/server/ParamertizedString.java
index 6fa2450fe4..6fa2450fe4 100644
--- a/src/main/java/com/google/gerrit/server/ParamertizedString.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ParamertizedString.java
diff --git a/src/main/java/com/google/gerrit/server/RemotePeer.java b/gerrit-server/src/main/java/com/google/gerrit/server/RemotePeer.java
index 77e6d4371d..77e6d4371d 100644
--- a/src/main/java/com/google/gerrit/server/RemotePeer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/RemotePeer.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
new file mode 100644
index 0000000000..e6e7786a26
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ReplicationUser extends CurrentUser {
+ public interface Factory {
+ ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups);
+ }
+
+ private Set<AccountGroup.Id> effectiveGroups;
+
+ @Inject
+ protected ReplicationUser(AuthConfig authConfig,
+ @Assisted Set<AccountGroup.Id> authGroups) {
+ super(AccessPath.REPLICATION, authConfig);
+ effectiveGroups = new HashSet<AccountGroup.Id>(authGroups);
+
+ if (effectiveGroups.isEmpty()) {
+ effectiveGroups.addAll(authConfig.getRegisteredGroups());
+ }
+
+ effectiveGroups = Collections.unmodifiableSet(effectiveGroups);
+ }
+
+ @Override
+ public Set<AccountGroup.Id> getEffectiveGroups() {
+ return Collections.unmodifiableSet(effectiveGroups);
+ }
+
+ @Override
+ public Set<Change.Id> getStarredChanges() {
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/RequestCleanup.java b/gerrit-server/src/main/java/com/google/gerrit/server/RequestCleanup.java
index 23c0a6fd6d..23c0a6fd6d 100644
--- a/src/main/java/com/google/gerrit/server/RequestCleanup.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/RequestCleanup.java
diff --git a/src/main/java/com/google/gerrit/server/UrlEncoded.java b/gerrit-server/src/main/java/com/google/gerrit/server/UrlEncoded.java
index f1f6dcd520..f1f6dcd520 100644
--- a/src/main/java/com/google/gerrit/server/UrlEncoded.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/UrlEncoded.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
new file mode 100644
index 0000000000..1eb1a4f772
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+
+import java.util.Set;
+
+/** Translates an email address to a set of matching accounts. */
+public interface AccountByEmailCache {
+ public Set<Account.Id> get(String email);
+
+ public void evict(String email);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
new file mode 100644
index 0000000000..d4f7a4d2fc
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
@@ -0,0 +1,111 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Translates an email address to a set of matching accounts. */
+@Singleton
+public class AccountByEmailCacheImpl implements AccountByEmailCache {
+ private static final String CACHE_NAME = "accounts_byemail";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<String, Set<Account.Id>>> type =
+ new TypeLiteral<Cache<String, Set<Account.Id>>>() {};
+ core(type, CACHE_NAME);
+ bind(AccountByEmailCacheImpl.class);
+ bind(AccountByEmailCache.class).to(AccountByEmailCacheImpl.class);
+ }
+ };
+ }
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final SelfPopulatingCache<String, Set<Account.Id>> self;
+
+ @Inject
+ AccountByEmailCacheImpl(final SchemaFactory<ReviewDb> schema,
+ @Named(CACHE_NAME) final Cache<String, Set<Account.Id>> rawCache) {
+ this.schema = schema;
+ this.self = new SelfPopulatingCache<String, Set<Account.Id>>(rawCache) {
+ @Override
+ protected Set<Account.Id> createEntry(final String key) throws Exception {
+ return lookup(key);
+ }
+
+ @Override
+ protected Set<Account.Id> missing(final String key) {
+ return Collections.emptySet();
+ }
+ };
+ }
+
+ private Set<Account.Id> lookup(final String email) throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ final HashSet<Account.Id> r = new HashSet<Account.Id>();
+ for (Account a : db.accounts().byPreferredEmail(email)) {
+ r.add(a.getId());
+ }
+ for (AccountExternalId a : db.accountExternalIds().byEmailAddress(email)) {
+ r.add(a.getAccountId());
+ }
+ return pack(r);
+ } finally {
+ db.close();
+ }
+ }
+
+ public Set<Account.Id> get(final String email) {
+ return self.get(email);
+ }
+
+ public void evict(final String email) {
+ self.remove(email);
+ }
+
+ private static Set<Account.Id> pack(final Set<Account.Id> c) {
+ switch (c.size()) {
+ case 0:
+ return Collections.emptySet();
+ case 1:
+ return one(c);
+ default:
+ return Collections.unmodifiableSet(new HashSet<Account.Id>(c));
+ }
+ }
+
+ private static <T> Set<T> one(final Set<T> c) {
+ return Collections.singleton(c.iterator().next());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java
new file mode 100644
index 0000000000..e2370cd25d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCache.java
@@ -0,0 +1,24 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Caches important (but small) account state to avoid database hits. */
+public interface AccountCache {
+ public AccountState get(Account.Id accountId);
+
+ public void evict(Account.Id accountId);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
new file mode 100644
index 0000000000..4aaff7a577
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -0,0 +1,135 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Caches important (but small) account state to avoid database hits. */
+@Singleton
+public class AccountCacheImpl implements AccountCache {
+ private static final String CACHE_NAME = "accounts";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<Account.Id, AccountState>> type =
+ new TypeLiteral<Cache<Account.Id, AccountState>>() {};
+ core(type, CACHE_NAME);
+ bind(AccountCacheImpl.class);
+ bind(AccountCache.class).to(AccountCacheImpl.class);
+ }
+ };
+ }
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final GroupCache groupCache;
+ private final SelfPopulatingCache<Account.Id, AccountState> self;
+
+ private final Set<AccountGroup.Id> registered;
+ private final Set<AccountGroup.Id> anonymous;
+
+ @Inject
+ AccountCacheImpl(final SchemaFactory<ReviewDb> sf, final AuthConfig auth,
+ final GroupCache groupCache,
+ @Named(CACHE_NAME) final Cache<Account.Id, AccountState> rawCache) {
+ schema = sf;
+ registered = auth.getRegisteredGroups();
+ anonymous = auth.getAnonymousGroups();
+ this.groupCache = groupCache;
+
+ self = new SelfPopulatingCache<Account.Id, AccountState>(rawCache) {
+ @Override
+ protected AccountState createEntry(Account.Id key) throws Exception {
+ return lookup(key);
+ }
+
+ @Override
+ protected AccountState missing(final Account.Id key) {
+ return missingAccount(key);
+ }
+ };
+ }
+
+ private AccountState lookup(final Account.Id who) throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ final Account account = db.accounts().get(who);
+ if (account == null) {
+ // Account no longer exists? They are anonymous.
+ //
+ return missingAccount(who);
+ }
+
+ final Collection<AccountExternalId> externalIds =
+ Collections.unmodifiableCollection(db.accountExternalIds().byAccount(
+ who).toList());
+
+ Set<AccountGroup.Id> internalGroups = new HashSet<AccountGroup.Id>();
+ for (AccountGroupMember g : db.accountGroupMembers().byAccount(who)) {
+ final AccountGroup.Id groupId = g.getAccountGroupId();
+ final AccountGroup group = groupCache.get(groupId);
+ if (group != null && group.getType() == AccountGroup.Type.INTERNAL) {
+ internalGroups.add(groupId);
+ }
+ }
+
+ if (internalGroups.isEmpty()) {
+ internalGroups = registered;
+ } else {
+ internalGroups.addAll(registered);
+ internalGroups = Collections.unmodifiableSet(internalGroups);
+ }
+
+ return new AccountState(account, internalGroups, externalIds);
+ } finally {
+ db.close();
+ }
+ }
+
+ private AccountState missingAccount(final Account.Id accountId) {
+ final Account account = new Account(accountId);
+ final Collection<AccountExternalId> ids = Collections.emptySet();
+ return new AccountState(account, anonymous, ids);
+ }
+
+ public AccountState get(final Account.Id accountId) {
+ return self.get(accountId);
+ }
+
+ public void evict(final Account.Id accountId) {
+ self.remove(accountId);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountException.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountException.java
index 2728a62f30..2728a62f30 100644
--- a/src/main/java/com/google/gerrit/server/account/AccountException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountException.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java
new file mode 100644
index 0000000000..bb6e278e3e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java
@@ -0,0 +1,75 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.common.data.AccountInfo;
+import com.google.gerrit.common.data.AccountInfoCache;
+import com.google.gerrit.reviewdb.Account;
+import com.google.inject.Inject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Efficiently builds an {@link AccountInfoCache}. */
+public class AccountInfoCacheFactory {
+ public interface Factory {
+ AccountInfoCacheFactory create();
+ }
+
+ private final AccountCache accountCache;
+ private final Map<Account.Id, Account> out;
+
+ @Inject
+ AccountInfoCacheFactory(final AccountCache accountCache) {
+ this.accountCache = accountCache;
+ this.out = new HashMap<Account.Id, Account>();
+ }
+
+ /**
+ * Indicate an account will be needed later on.
+ *
+ * @param id identity that will be needed in the future; may be null.
+ */
+ public void want(final Account.Id id) {
+ if (id != null && !out.containsKey(id)) {
+ out.put(id, accountCache.get(id).getAccount());
+ }
+ }
+
+ /** Indicate one or more accounts will be needed later on. */
+ public void want(final Iterable<Account.Id> ids) {
+ for (final Account.Id id : ids) {
+ want(id);
+ }
+ }
+
+ public Account get(final Account.Id id) {
+ want(id);
+ return out.get(id);
+ }
+
+ /**
+ * Create an AccountInfoCache with the currently loaded Account entities.
+ * */
+ public AccountInfoCache create() {
+ final List<AccountInfo> r = new ArrayList<AccountInfo>(out.size());
+ for (final Account a : out.values()) {
+ r.add(new AccountInfo(a));
+ }
+ return new AccountInfoCache(r);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
new file mode 100644
index 0000000000..f4c4b7b672
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -0,0 +1,301 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Tracks authentication related details for user accounts. */
+@Singleton
+public class AccountManager {
+ private final SchemaFactory<ReviewDb> schema;
+ private final AccountCache byIdCache;
+ private final AccountByEmailCache byEmailCache;
+ private final AuthConfig authConfig;
+ private final Realm realm;
+
+ @Inject
+ AccountManager(final SchemaFactory<ReviewDb> schema,
+ final AccountCache byIdCache, final AccountByEmailCache byEmailCache,
+ final AuthConfig authConfig, final Realm accountMapper) {
+ this.schema = schema;
+ this.byIdCache = byIdCache;
+ this.byEmailCache = byEmailCache;
+ this.authConfig = authConfig;
+ this.realm = accountMapper;
+ }
+
+ /**
+ * @return user identified by this external identity string, or null.
+ */
+ public Account.Id lookup(final String externalId) throws AccountException {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final AccountExternalId ext =
+ db.accountExternalIds().get(new AccountExternalId.Key(externalId));
+ return ext != null ? ext.getAccountId() : null;
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new AccountException("Cannot lookup account " + externalId, e);
+ }
+ }
+
+ /**
+ * Authenticate the user, potentially creating a new account if they are new.
+ *
+ * @param who identity of the user, with any details we received about them.
+ * @return the result of authenticating the user.
+ * @throws AccountException the account does not exist, and cannot be created,
+ * or exists, but cannot be located.
+ */
+ public AuthResult authenticate(AuthRequest who) throws AccountException {
+ who = realm.authenticate(who);
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final AccountExternalId id =
+ db.accountExternalIds().get(
+ new AccountExternalId.Key(who.getExternalId()));
+ if (id == null) {
+ // New account, automatically create and return.
+ //
+ return create(db, who);
+
+ } else {
+ // Account exists, return the identity to the caller.
+ //
+ update(db, who, id);
+ return new AuthResult(id.getAccountId(), false);
+ }
+
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new AccountException("Authentication error", e);
+ }
+ }
+
+ private void update(final ReviewDb db, final AuthRequest who,
+ final AccountExternalId extId) throws OrmException, AccountException {
+ final Transaction txn = db.beginTransaction();
+ final Account account = db.accounts().get(extId.getAccountId());
+ boolean updateAccount = false;
+ if (account == null) {
+ throw new AccountException("Account has been deleted");
+ }
+
+ // If the email address was modified by the authentication provider,
+ // update our records to match the changed email.
+ //
+ final String newEmail = who.getEmailAddress();
+ final String oldEmail = extId.getEmailAddress();
+ if (newEmail != null && !newEmail.equals(oldEmail)) {
+ if (oldEmail != null && oldEmail.equals(account.getPreferredEmail())) {
+ updateAccount = true;
+ account.setPreferredEmail(newEmail);
+ }
+
+ extId.setEmailAddress(newEmail);
+ }
+
+ if (!realm.allowsEdit(Account.FieldName.FULL_NAME)
+ && !eq(account.getFullName(), who.getDisplayName())) {
+ updateAccount = true;
+ account.setFullName(who.getDisplayName());
+ }
+ if (!realm.allowsEdit(Account.FieldName.SSH_USER_NAME)
+ && !eq(account.getSshUserName(), who.getSshUserName())) {
+ updateAccount = true;
+ account.setSshUserName(who.getSshUserName());
+ }
+
+ extId.setLastUsedOn();
+ db.accountExternalIds().update(Collections.singleton(extId), txn);
+ if (updateAccount) {
+ db.accounts().update(Collections.singleton(account), txn);
+ }
+ txn.commit();
+
+ if (newEmail != null && !newEmail.equals(oldEmail)) {
+ byEmailCache.evict(oldEmail);
+ byEmailCache.evict(newEmail);
+ }
+ if (updateAccount) {
+ byIdCache.evict(account.getId());
+ }
+ }
+
+ private static boolean eq(final String a, final String b) {
+ return (a == null && b == null) || (a != null && a.equals(b));
+ }
+
+ private AuthResult create(final ReviewDb db, final AuthRequest who)
+ throws OrmException, AccountException {
+ if (authConfig.isAllowGoogleAccountUpgrade()
+ && who.isScheme(OpenIdUrls.URL_GOOGLE + "?")
+ && who.getEmailAddress() != null) {
+ final List<AccountExternalId> openId = new ArrayList<AccountExternalId>();
+ final List<AccountExternalId> v1 = new ArrayList<AccountExternalId>();
+
+ for (final AccountExternalId extId : db.accountExternalIds()
+ .byEmailAddress(who.getEmailAddress())) {
+ if (extId.isScheme(OpenIdUrls.URL_GOOGLE + "?")) {
+ openId.add(extId);
+ } else if (extId.isScheme(AccountExternalId.LEGACY_GAE)) {
+ v1.add(extId);
+ }
+ }
+
+ if (!openId.isEmpty()) {
+ // The user has already registered with an OpenID from Google, but
+ // Google may have changed the user's OpenID identity if this server
+ // name has changed. Insert a new identity for the user.
+ //
+ final Account.Id accountId = openId.get(0).getAccountId();
+
+ if (openId.size() > 1) {
+ // Validate all matching identities are actually the same user.
+ //
+ for (final AccountExternalId extId : openId) {
+ if (!accountId.equals(extId.getAccountId())) {
+ throw new AccountException("Multiple user accounts for "
+ + who.getEmailAddress() + " using Google Accounts provider");
+ }
+ }
+ }
+
+ final AccountExternalId newId = createId(accountId, who);
+ newId.setEmailAddress(who.getEmailAddress());
+ newId.setLastUsedOn();
+
+ if (openId.size() == 1) {
+ final AccountExternalId oldId = openId.get(0);
+ final Transaction txn = db.beginTransaction();
+ db.accountExternalIds().delete(Collections.singleton(oldId), txn);
+ db.accountExternalIds().insert(Collections.singleton(newId), txn);
+ txn.commit();
+ } else {
+ db.accountExternalIds().insert(Collections.singleton(newId));
+ }
+ return new AuthResult(accountId, false);
+
+ } else if (v1.size() == 1) {
+ // Exactly one user was imported from Gerrit 1.x with this email
+ // address. Upgrade their account by deleting the legacy import
+ // identity and creating a new identity matching the token we have.
+ //
+ final AccountExternalId oldId = v1.get(0);
+ final AccountExternalId newId = createId(oldId.getAccountId(), who);
+ newId.setEmailAddress(who.getEmailAddress());
+ newId.setLastUsedOn();
+ final Transaction txn = db.beginTransaction();
+ db.accountExternalIds().delete(Collections.singleton(oldId), txn);
+ db.accountExternalIds().insert(Collections.singleton(newId), txn);
+ txn.commit();
+ return new AuthResult(newId.getAccountId(), false);
+
+ } else if (v1.size() > 1) {
+ throw new AccountException("Multiple Gerrit 1.x accounts found");
+ }
+ }
+
+ final Account.Id newId = new Account.Id(db.nextAccountId());
+ final Account account = new Account(newId);
+ final AccountExternalId extId = createId(newId, who);
+
+ extId.setLastUsedOn();
+ extId.setEmailAddress(who.getEmailAddress());
+ account.setFullName(who.getDisplayName());
+ account.setPreferredEmail(extId.getEmailAddress());
+
+ if (who.getSshUserName() != null
+ && db.accounts().bySshUserName(who.getSshUserName()) == null) {
+ // Only set if the name hasn't been used yet, but was given to us.
+ //
+ account.setSshUserName(who.getSshUserName());
+ }
+
+ final Transaction txn = db.beginTransaction();
+ db.accounts().insert(Collections.singleton(account), txn);
+ db.accountExternalIds().insert(Collections.singleton(extId), txn);
+ txn.commit();
+
+ byEmailCache.evict(account.getPreferredEmail());
+ realm.onCreateAccount(who, account);
+ return new AuthResult(newId, true);
+ }
+
+ private static AccountExternalId createId(final Account.Id newId,
+ final AuthRequest who) {
+ final String ext = who.getExternalId();
+ return new AccountExternalId(newId, new AccountExternalId.Key(ext));
+ }
+
+ /**
+ * Link another authentication identity to an existing account.
+ *
+ * @param to account to link the identity onto.
+ * @param who the additional identity.
+ * @throws AccountException the identity belongs to a different account, or it
+ * cannot be linked at this time.
+ */
+ public void link(final Account.Id to, final AuthRequest who)
+ throws AccountException {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ AccountExternalId extId =
+ db.accountExternalIds().get(
+ new AccountExternalId.Key(who.getExternalId()));
+ if (extId != null) {
+ if (!extId.getAccountId().equals(to)) {
+ throw new AccountException("Identity in use by another account");
+ }
+ update(db, who, extId);
+ } else {
+ extId = createId(to, who);
+ extId.setEmailAddress(who.getEmailAddress());
+ extId.setLastUsedOn();
+ db.accountExternalIds().insert(Collections.singleton(extId));
+ if (who.getEmailAddress() != null) {
+ byEmailCache.evict(who.getEmailAddress());
+ byIdCache.evict(to);
+ }
+ }
+
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new AccountException("Cannot link identity", e);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
new file mode 100644
index 0000000000..bc548d7223
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
@@ -0,0 +1,86 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.ResultSet;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.List;
+import java.util.Set;
+
+public class AccountResolver {
+ private final Realm realm;
+ private final AccountByEmailCache byEmail;
+ private final AccountCache byId;
+ private final Provider<ReviewDb> schema;
+
+ @Inject
+ AccountResolver(final Realm realm, final AccountByEmailCache byEmail,
+ final AccountCache byId, final Provider<ReviewDb> schema) {
+ this.realm = realm;
+ this.byEmail = byEmail;
+ this.byId = byId;
+ this.schema = schema;
+ }
+
+ /**
+ * Locate exactly one account matching the name or name/email string.
+ *
+ * @param nameOrEmail a string of the format
+ * "Full Name &lt;email@example&gt;", or just the email address
+ * ("email@example"), or a full name, or an account id.
+ * @return the single account that matches; null if no account matches or
+ * there are multiple candidates.
+ */
+ public Account find(final String nameOrEmail) throws OrmException {
+ if (nameOrEmail.matches("^[1-9][0-9]*$")) {
+ return byId.get(Account.Id.parse(nameOrEmail)).getAccount();
+ }
+
+ final int lt = nameOrEmail.indexOf('<');
+ final int gt = nameOrEmail.indexOf('>');
+ if (lt >= 0 && gt > lt && nameOrEmail.contains("@")) {
+ return findByEmail(nameOrEmail.substring(lt + 1, gt));
+ }
+
+ if (nameOrEmail.contains("@")) {
+ return findByEmail(nameOrEmail);
+ }
+
+ final Account.Id id = realm.lookup(nameOrEmail);
+ if (id != null) {
+ return byId.get(id).getAccount();
+ }
+
+ return oneAccount(schema.get().accounts().byFullName(nameOrEmail));
+ }
+
+ private Account findByEmail(final String email) {
+ final Set<Account.Id> candidates = byEmail.get(email);
+ if (1 == candidates.size()) {
+ return byId.get(candidates.iterator().next()).getAccount();
+ }
+ return null;
+ }
+
+ private static Account oneAccount(final ResultSet<Account> rs) {
+ final List<Account> r = rs.toList();
+ return r.size() == 1 ? r.get(0) : null;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
new file mode 100644
index 0000000000..4c8103a187
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class AccountState {
+ private final Account account;
+ private final Set<AccountGroup.Id> internalGroups;
+ private final Collection<AccountExternalId> externalIds;
+
+ public AccountState(final Account account,
+ final Set<AccountGroup.Id> actualGroups,
+ final Collection<AccountExternalId> externalIds) {
+ this.account = account;
+ this.internalGroups = actualGroups;
+ this.externalIds = externalIds;
+ }
+
+ /** Get the cached account metadata. */
+ public Account getAccount() {
+ return account;
+ }
+
+ /**
+ * All email addresses registered to this account.
+ * <p>
+ * Gerrit is "reasonably certain" that the returned email addresses actually
+ * belong to the user of the account. Some emails may have been obtained from
+ * the authentication provider, which in the case of OpenID may be trusting
+ * the provider to have validated the address. Other emails may have been
+ * validated by Gerrit directly.
+ */
+ public Set<String> getEmailAddresses() {
+ final Set<String> emails = new HashSet<String>();
+ for (final AccountExternalId e : externalIds) {
+ if (e.getEmailAddress() != null && !e.getEmailAddress().isEmpty()) {
+ emails.add(e.getEmailAddress());
+ }
+ }
+ return emails;
+ }
+
+ /** The external identities that identify the account holder. */
+ public Collection<AccountExternalId> getExternalIds() {
+ return externalIds;
+ }
+
+ /** The set of groups maintained directly within the Gerrit database. */
+ public Set<AccountGroup.Id> getInternalGroups() {
+ return internalGroups;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java
new file mode 100644
index 0000000000..060224613f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_GERRIT;
+import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_MAILTO;
+
+/**
+ * Information for {@link AccountManager#authenticate(AuthRequest)}.
+ * <p>
+ * Callers should populate this object with as much information as possible
+ * about the user account. For example, OpenID authentication might return
+ * registration information including a display name for the user, and an email
+ * address for them. These fields however are optional, as not all OpenID
+ * providers return them, and not all non-OpenID systems can use them.
+ */
+public class AuthRequest {
+ /** Create a request for a local username, such as from LDAP. */
+ public static AuthRequest forUser(final String username) {
+ final AuthRequest r;
+ r = new AuthRequest(SCHEME_GERRIT + username);
+ r.setSshUserName(username);
+ return r;
+ }
+
+ /**
+ * Create a request for an email address registration.
+ * <p>
+ * This type of request should be used only to attach a new email address to
+ * an existing user account.
+ */
+ public static AuthRequest forEmail(final String email) {
+ final AuthRequest r;
+ r = new AuthRequest(SCHEME_MAILTO + email);
+ r.setEmailAddress(email);
+ return r;
+ }
+
+ private final String externalId;
+ private String password;
+ private String displayName;
+ private String emailAddress;
+ private String sshUserName;
+
+ public AuthRequest(final String externalId) {
+ this.externalId = externalId;
+ }
+
+ public String getExternalId() {
+ return externalId;
+ }
+
+ public boolean isScheme(final String scheme) {
+ return getExternalId().startsWith(scheme);
+ }
+
+ public String getLocalUser() {
+ if (isScheme(SCHEME_GERRIT)) {
+ return getExternalId().substring(SCHEME_GERRIT.length());
+ }
+ return null;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(final String pass) {
+ password = pass;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(final String name) {
+ displayName = name != null && name.length() > 0 ? name : null;
+ }
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public void setEmailAddress(final String email) {
+ emailAddress = email != null && email.length() > 0 ? email : null;
+ }
+
+ public String getSshUserName() {
+ return sshUserName;
+ }
+
+ public void setSshUserName(final String user) {
+ sshUserName = user;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthResult.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthResult.java
new file mode 100644
index 0000000000..569216be84
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthResult.java
@@ -0,0 +1,43 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Result from {@link AccountManager#authenticate(AuthRequest)}. */
+public class AuthResult {
+ private final Account.Id accountId;
+ private final boolean isNew;
+
+ AuthResult(final Account.Id accountId, final boolean isNew) {
+ this.accountId = accountId;
+ this.isNew = isNew;
+ }
+
+ /** Identity of the user account that was authenticated into. */
+ public Account.Id getAccountId() {
+ return accountId;
+ }
+
+ /**
+ * True if this account was recently created for the user.
+ * <p>
+ * New users should be redirected to the registration screen, so they can
+ * configure their new user account.
+ */
+ public boolean isNew() {
+ return isNew;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
new file mode 100644
index 0000000000..a836f543dc
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.inject.Inject;
+
+import java.util.Collections;
+import java.util.Set;
+
+public final class DefaultRealm implements Realm {
+ private final EmailExpander emailExpander;
+ private final AccountByEmailCache byEmail;
+
+ @Inject
+ DefaultRealm(final EmailExpander emailExpander,
+ final AccountByEmailCache byEmail) {
+ this.emailExpander = emailExpander;
+ this.byEmail = byEmail;
+ }
+
+ @Override
+ public boolean allowsEdit(final Account.FieldName field) {
+ return true;
+ }
+
+ @Override
+ public AuthRequest authenticate(final AuthRequest who) {
+ if (who.getEmailAddress() == null && who.getLocalUser() != null
+ && emailExpander.canExpand(who.getLocalUser())) {
+ who.setEmailAddress(emailExpander.expand(who.getLocalUser()));
+ }
+ return who;
+ }
+
+ @Override
+ public void onCreateAccount(final AuthRequest who, final Account account) {
+ }
+
+ @Override
+ public Set<AccountGroup.Id> groups(final AccountState who) {
+ return who.getInternalGroups();
+ }
+
+ @Override
+ public Account.Id lookup(final String accountName) {
+ if (emailExpander.canExpand(accountName)) {
+ final Set<Account.Id> c = byEmail.get(emailExpander.expand(accountName));
+ if (1 == c.size()) {
+ return c.iterator().next();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
+ return Collections.emptySet();
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/account/EmailExpander.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/EmailExpander.java
index 59091e66d8..59091e66d8 100644
--- a/src/main/java/com/google/gerrit/server/account/EmailExpander.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/EmailExpander.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
new file mode 100644
index 0000000000..880ae2cc49
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
@@ -0,0 +1,30 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+
+/** Tracks group objects in memory for efficient access. */
+public interface GroupCache {
+ public AccountGroup get(AccountGroup.Id groupId);
+
+ public AccountGroup get(AccountGroup.ExternalNameKey externalName);
+
+ public void evict(AccountGroup group);
+
+ public void evictAfterRename(AccountGroup.NameKey oldName);
+
+ public AccountGroup lookup(String groupName);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
new file mode 100644
index 0000000000..74e3218453
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -0,0 +1,163 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+/** Tracks group objects in memory for efficient access. */
+@Singleton
+public class GroupCacheImpl implements GroupCache {
+ private static final String CACHE_NAME = "groups";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<com.google.gwtorm.client.Key<?>, AccountGroup>> byId =
+ new TypeLiteral<Cache<com.google.gwtorm.client.Key<?>, AccountGroup>>() {};
+ core(byId, CACHE_NAME);
+ bind(GroupCacheImpl.class);
+ bind(GroupCache.class).to(GroupCacheImpl.class);
+ }
+ };
+ }
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final AccountGroup.Id administrators;
+ private final SelfPopulatingCache<AccountGroup.Id, AccountGroup> byId;
+ private final SelfPopulatingCache<AccountGroup.NameKey, AccountGroup> byName;
+ private final SelfPopulatingCache<AccountGroup.ExternalNameKey, AccountGroup> byExternalName;
+
+ @Inject
+ GroupCacheImpl(
+ final SchemaFactory<ReviewDb> sf,
+ final AuthConfig authConfig,
+ @Named(CACHE_NAME) final Cache<com.google.gwtorm.client.Key<?>, AccountGroup> rawAny) {
+ schema = sf;
+ administrators = authConfig.getAdministratorsGroup();
+
+ byId =
+ new SelfPopulatingCache<AccountGroup.Id, AccountGroup>((Cache) rawAny) {
+ @Override
+ public AccountGroup createEntry(final AccountGroup.Id key)
+ throws Exception {
+ return lookup(key);
+ }
+
+ @Override
+ protected AccountGroup missing(final AccountGroup.Id key) {
+ return missingGroup(key);
+ }
+ };
+
+ byName =
+ new SelfPopulatingCache<AccountGroup.NameKey, AccountGroup>(
+ (Cache) rawAny) {
+ @Override
+ public AccountGroup createEntry(final AccountGroup.NameKey key)
+ throws Exception {
+ return lookup(key);
+ }
+ };
+
+ byExternalName =
+ new SelfPopulatingCache<AccountGroup.ExternalNameKey, AccountGroup>(
+ (Cache) rawAny) {
+ @Override
+ public AccountGroup createEntry(final AccountGroup.ExternalNameKey key)
+ throws Exception {
+ return lookup(key);
+ }
+ };
+ }
+
+ private AccountGroup lookup(final AccountGroup.Id groupId)
+ throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ final AccountGroup group = db.accountGroups().get(groupId);
+ if (group != null) {
+ return group;
+ } else {
+ return missingGroup(groupId);
+ }
+ } finally {
+ db.close();
+ }
+ }
+
+ private AccountGroup missingGroup(final AccountGroup.Id groupId) {
+ final AccountGroup.NameKey name =
+ new AccountGroup.NameKey("Deleted Group" + groupId.toString());
+ final AccountGroup g = new AccountGroup(name, groupId);
+ g.setType(AccountGroup.Type.SYSTEM);
+ g.setOwnerGroupId(administrators);
+ return g;
+ }
+
+ private AccountGroup lookup(final AccountGroup.NameKey groupName)
+ throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ return db.accountGroups().get(groupName);
+ } finally {
+ db.close();
+ }
+ }
+
+ private AccountGroup lookup(final AccountGroup.ExternalNameKey externalName)
+ throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ return db.accountGroups().get(externalName);
+ } finally {
+ db.close();
+ }
+ }
+
+ public AccountGroup get(final AccountGroup.Id groupId) {
+ return byId.get(groupId);
+ }
+
+ public void evict(final AccountGroup group) {
+ byId.remove(group.getId());
+ byName.remove(group.getNameKey());
+ byExternalName.remove(group.getExternalNameKey());
+ }
+
+ public void evictAfterRename(final AccountGroup.NameKey oldName) {
+ byName.remove(oldName);
+ }
+
+ public AccountGroup lookup(final String groupName) {
+ return byName.get(new AccountGroup.NameKey(groupName));
+ }
+
+ public AccountGroup get(final AccountGroup.ExternalNameKey externalName) {
+ return byExternalName.get(externalName);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
new file mode 100644
index 0000000000..40360b9f77
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/** Access control management for a group of accounts managed in Gerrit. */
+public class GroupControl {
+ public static class Factory {
+ private final GroupCache groupCache;
+ private final Provider<CurrentUser> user;
+
+ @Inject
+ Factory(final GroupCache gc, final Provider<CurrentUser> cu) {
+ groupCache = gc;
+ user = cu;
+ }
+
+ public GroupControl controlFor(final AccountGroup.Id groupId)
+ throws NoSuchGroupException {
+ final AccountGroup group = groupCache.get(groupId);
+ if (group == null) {
+ throw new NoSuchGroupException(groupId);
+ }
+ return new GroupControl(user.get(), group);
+ }
+
+ public GroupControl validateFor(final AccountGroup.Id groupId)
+ throws NoSuchGroupException {
+ final GroupControl c = controlFor(groupId);
+ if (!c.isVisible()) {
+ throw new NoSuchGroupException(groupId);
+ }
+ return c;
+ }
+ }
+
+ private final CurrentUser user;
+ private final AccountGroup group;
+
+ GroupControl(final CurrentUser who, final AccountGroup gc) {
+ user = who;
+ group = gc;
+ }
+
+ public CurrentUser getCurrentUser() {
+ return user;
+ }
+
+ public AccountGroup getAccountGroup() {
+ return group;
+ }
+
+ /** Can this user see this group exists? */
+ public boolean isVisible() {
+ return isOwner();
+ }
+
+ public boolean isOwner() {
+ final AccountGroup.Id owner = group.getOwnerGroupId();
+ return getCurrentUser().getEffectiveGroups().contains(owner)
+ || getCurrentUser().isAdministrator();
+ }
+
+ public boolean canAdd(final Account.Id id) {
+ return isOwner();
+ }
+
+ public boolean canRemove(final Account.Id id) {
+ return isOwner();
+ }
+
+ public boolean canSee(Account.Id id) {
+ return isOwner();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java
new file mode 100644
index 0000000000..5cd2003442
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+
+/** Indicates the account group does not exist. */
+public class NoSuchGroupException extends Exception {
+ public NoSuchGroupException(final AccountGroup.Id key) {
+ this(key, null);
+ }
+
+ public NoSuchGroupException(final AccountGroup.Id key, final Throwable why) {
+ super(key.toString(), why);
+ }
+
+ public NoSuchGroupException(final AccountGroup.NameKey k) {
+ this(k, null);
+ }
+
+ public NoSuchGroupException(final AccountGroup.NameKey k, final Throwable why) {
+ super(k.toString(), why);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
new file mode 100644
index 0000000000..e42d4ae322
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.account;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+
+import java.util.Set;
+
+public interface Realm {
+ /** Can the end-user modify this field of their own account? */
+ public boolean allowsEdit(Account.FieldName field);
+
+ public AuthRequest authenticate(AuthRequest who) throws AccountException;
+
+ public void onCreateAccount(AuthRequest who, Account account);
+
+ public Set<AccountGroup.Id> groups(AccountState who);
+
+ /**
+ * Locate an account whose local username is the given account name.
+ * <p>
+ * Generally this only works for local realms, such as one backed by an LDAP
+ * directory, or where there is an {@link EmailExpander} configured that knows
+ * how to convert the accountName into an email address, and then locate the
+ * user by that email address.
+ */
+ public Account.Id lookup(String accountName);
+
+ /**
+ * Search for matching external groups.
+ */
+ public Set<AccountGroup.ExternalNameKey> lookupGroups(String name);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
new file mode 100644
index 0000000000..352e4664e0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.auth.ldap;
+
+import static java.util.concurrent.TimeUnit.HOURS;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+
+import java.util.Set;
+
+public class LdapModule extends CacheModule {
+ static final String USERNAME_CACHE = "ldap_usernames";
+ static final String GROUP_CACHE = "ldap_groups";
+
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<String, Set<AccountGroup.Id>>> groups =
+ new TypeLiteral<Cache<String, Set<AccountGroup.Id>>>() {};
+ final TypeLiteral<Cache<String, Account.Id>> usernames =
+ new TypeLiteral<Cache<String, Account.Id>>() {};
+
+ core(groups, GROUP_CACHE).maxAge(1, HOURS);
+ core(usernames, USERNAME_CACHE);
+ bind(Realm.class).to(LdapRealm.class).in(Scopes.SINGLETON);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java
new file mode 100644
index 0000000000..868e26a888
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapQuery.java
@@ -0,0 +1,130 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.auth.ldap;
+
+import com.google.gerrit.server.ParamertizedString;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+/** Supports issuing parameterized queries against an LDAP data source. */
+class LdapQuery {
+ static final Set<String> ALL_ATTRIBUTES = null;
+
+ private final String base;
+ private final SearchScope searchScope;
+ private final ParamertizedString pattern;
+ private final String[] returnAttributes;
+
+ LdapQuery(final String base, final SearchScope searchScope,
+ final ParamertizedString pattern, final Set<String> returnAttributes) {
+ this.base = base;
+ this.searchScope = searchScope;
+
+ this.pattern = pattern;
+
+ if (returnAttributes != null) {
+ this.returnAttributes = new String[returnAttributes.size()];
+ returnAttributes.toArray(this.returnAttributes);
+ } else {
+ this.returnAttributes = null;
+ }
+ }
+
+ List<String> getParameters() {
+ return pattern.getParameterNames();
+ }
+
+ List<Result> query(final DirContext ctx, final Map<String, String> params)
+ throws NamingException {
+ final SearchControls sc = new SearchControls();
+ final NamingEnumeration<SearchResult> res;
+
+ sc.setSearchScope(searchScope.scope());
+ sc.setReturningAttributes(returnAttributes);
+ res = ctx.search(base, pattern.getRawPattern(), pattern.bind(params), sc);
+ try {
+ final List<Result> r = new ArrayList<Result>();
+ while (res.hasMore()) {
+ r.add(new Result(res.next()));
+ }
+ return r;
+ } finally {
+ res.close();
+ }
+ }
+
+ class Result {
+ private final Map<String, Attribute> atts = new HashMap<String, Attribute>();
+
+ Result(final SearchResult sr) {
+ if (returnAttributes != null) {
+ for (final String attName : returnAttributes) {
+ final Attribute a = sr.getAttributes().get(attName);
+ if (a != null && a.size() > 0) {
+ atts.put(attName, a);
+ }
+ }
+
+ } else {
+ NamingEnumeration<? extends Attribute> e = sr.getAttributes().getAll();
+ while (e.hasMoreElements()) {
+ final Attribute a = e.nextElement();
+ atts.put(a.getID(), a);
+ }
+ }
+
+ atts.put("dn", new BasicAttribute("dn", sr.getNameInNamespace()));
+ }
+
+ String getDN() throws NamingException {
+ return get("dn");
+ }
+
+ String get(final String attName) throws NamingException {
+ final Attribute att = getAll(attName);
+ return att != null && 0 < att.size() ? String.valueOf(att.get(0)) : null;
+ }
+
+ Attribute getAll(final String attName) {
+ return atts.get(attName);
+ }
+
+ Set<String> attributes() {
+ return Collections.unmodifiableSet(atts.keySet());
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return getDN();
+ } catch (NamingException e) {
+ return "";
+ }
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
new file mode 100644
index 0000000000..1eaa55cf35
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -0,0 +1,587 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.auth.ldap;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ParamertizedString;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.account.EmailExpander;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.util.ssl.BlindSSLSocketFactory;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.net.ssl.SSLSocketFactory;
+
+@Singleton
+class LdapRealm implements Realm {
+ private static final Logger log = LoggerFactory.getLogger(LdapRealm.class);
+ private static final String LDAP = "com.sun.jndi.ldap.LdapCtxFactory";
+ private static final String USERNAME = "username";
+ private static final String GROUPNAME = "groupname";
+
+ private final String server;
+ private final String username;
+ private final String password;
+ private final LdapType type;
+ private final boolean sslVerify;
+
+ private final AuthConfig authConfig;
+ private final SchemaFactory<ReviewDb> schema;
+ private final EmailExpander emailExpander;
+ private final ParamertizedString accountFullName;
+ private final ParamertizedString accountEmailAddress;
+ private final ParamertizedString accountSshUserName;
+ private final String accountMemberField;
+ private final List<LdapQuery> accountQueryList;
+ private final SelfPopulatingCache<String, Account.Id> usernameCache;
+
+ private final GroupCache groupCache;
+ private boolean groupNeedsAccount;
+ private final List<String> groupBases;
+ private final SearchScope groupScope;
+ private final ParamertizedString groupPattern;
+ private final List<LdapQuery> groupMemberQueryList;
+ private final SelfPopulatingCache<String, Set<AccountGroup.Id>> membershipCache;
+
+ @Inject
+ LdapRealm(
+ final AuthConfig authConfig,
+ final GroupCache groupCache,
+ final EmailExpander emailExpander,
+ final SchemaFactory<ReviewDb> schema,
+ @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.Id>> rawGroup,
+ @Named(LdapModule.USERNAME_CACHE) final Cache<String, Account.Id> rawUsername,
+ @GerritServerConfig final Config config) {
+ this.authConfig = authConfig;
+ this.groupCache = groupCache;
+ this.emailExpander = emailExpander;
+ this.schema = schema;
+
+ this.server = required(config, "server");
+ this.username = optional(config, "username");
+ this.password = optional(config, "password");
+ this.sslVerify = config.getBoolean("ldap", "sslverify", true);
+ this.type = discoverLdapType();
+
+ groupMemberQueryList = new ArrayList<LdapQuery>();
+ accountQueryList = new ArrayList<LdapQuery>();
+
+ final Set<String> accountAtts = new HashSet<String>();
+
+ // Group query
+ //
+
+ groupBases = optionalList(config, "groupBase");
+ groupScope = scope(config, "groupScope");
+ groupPattern =
+ paramString(config, "groupPattern", type.groupPattern());
+ final String groupMemberPattern =
+ optdef(config, "groupMemberPattern", type.groupMemberPattern());
+
+ for (String groupBase : groupBases) {
+ if (groupMemberPattern != null) {
+ final LdapQuery groupMemberQuery =
+ new LdapQuery(groupBase, groupScope, new ParamertizedString(
+ groupMemberPattern), Collections.<String> emptySet());
+ if (groupMemberQuery.getParameters().isEmpty()) {
+ throw new IllegalArgumentException(
+ "No variables in ldap.groupMemberPattern");
+ }
+
+ for (final String name : groupMemberQuery.getParameters()) {
+ if (!USERNAME.equals(name)) {
+ groupNeedsAccount = true;
+ accountAtts.add(name);
+ }
+ }
+
+ groupMemberQueryList.add(groupMemberQuery);
+ }
+ }
+
+ membershipCache =
+ new SelfPopulatingCache<String, Set<AccountGroup.Id>>(rawGroup) {
+ @Override
+ public Set<AccountGroup.Id> createEntry(final String username)
+ throws Exception {
+ return queryForGroups(username);
+ }
+
+ @Override
+ protected Set<AccountGroup.Id> missing(final String key) {
+ return Collections.emptySet();
+ }
+ };
+
+ // Account query
+ //
+ accountFullName = paramString(config, "accountFullName", type.accountFullName());
+ if (accountFullName != null) {
+ accountAtts.addAll(accountFullName.getParameterNames());
+ }
+ accountEmailAddress = paramString(config, "accountEmailAddress", type.accountEmailAddress());
+ if (accountEmailAddress != null) {
+ accountAtts.addAll(accountEmailAddress.getParameterNames());
+ }
+ accountSshUserName = paramString(config, "accountSshUserName", type.accountSshUserName());
+ if (accountSshUserName != null) {
+ accountAtts.addAll(accountSshUserName.getParameterNames());
+ }
+ accountMemberField = optdef(config, "accountMemberField", type.accountMemberField());
+ if (accountMemberField != null) {
+ accountAtts.add(accountMemberField);
+ }
+
+ final SearchScope accountScope = scope(config, "accountScope");
+ final String accountPattern =
+ reqdef(config, "accountPattern", type.accountPattern());
+
+ for (String accountBase : requiredList(config, "accountBase")) {
+ final LdapQuery accountQuery =
+ new LdapQuery(accountBase, accountScope, new ParamertizedString(
+ accountPattern), accountAtts);
+ if (accountQuery.getParameters().isEmpty()) {
+ throw new IllegalArgumentException(
+ "No variables in ldap.accountPattern");
+ }
+ accountQueryList.add(accountQuery);
+ }
+
+ usernameCache = new SelfPopulatingCache<String, Account.Id>(rawUsername) {
+ @Override
+ public Account.Id createEntry(final String username) throws Exception {
+ return queryForUsername(username);
+ }
+ };
+ }
+
+ private static SearchScope scope(final Config c, final String setting) {
+ return ConfigUtil.getEnum(c, "ldap", null, setting, SearchScope.SUBTREE);
+ }
+
+ private static String optional(final Config config, final String name) {
+ return config.getString("ldap", null, name);
+ }
+
+ private static String required(final Config config, final String name) {
+ final String v = optional(config, name);
+ if (v == null || "".equals(v)) {
+ throw new IllegalArgumentException("No ldap." + name + " configured");
+ }
+ return v;
+ }
+
+ private static List<String> optionalList(final Config config,
+ final String name) {
+ String s[] = config.getStringList("ldap", null, name);
+ return Arrays.asList(s);
+ }
+
+ private static List<String> requiredList(final Config config,
+ final String name) {
+ List<String> vlist = optionalList(config, name);
+
+ if (vlist.isEmpty()) {
+ throw new IllegalArgumentException("No ldap " + name + " configured");
+ }
+
+ return vlist;
+ }
+
+ private static String optdef(final Config c, final String n, final String d) {
+ final String[] v = c.getStringList("ldap", null, n);
+ if (v == null || v.length == 0) {
+ return d;
+
+ } else if (v[0] == null || "".equals(v[0])) {
+ return null;
+
+ } else {
+ return v[0];
+ }
+ }
+
+ private static String reqdef(final Config c, final String n, final String d) {
+ final String v = optdef(c, n, d);
+ if (v == null) {
+ throw new IllegalArgumentException("No ldap." + n + " configured");
+ }
+ return v;
+ }
+
+ private static ParamertizedString paramString(Config c, String n, String d) {
+ String expression = optdef(c, n, d);
+ if (expression == null) {
+ return null;
+ } else if (expression.contains("${")) {
+ return new ParamertizedString(expression);
+ } else {
+ return new ParamertizedString("${" + expression + "}");
+ }
+ }
+
+ @Override
+ public boolean allowsEdit(final Account.FieldName field) {
+ switch (field) {
+ case FULL_NAME:
+ return accountFullName == null; // only if not obtained from LDAP
+
+ case SSH_USER_NAME:
+ return accountSshUserName == null; // only if not obtained from LDAP
+
+ default:
+ return true;
+ }
+ }
+
+ private static String apply(ParamertizedString p, LdapQuery.Result m)
+ throws NamingException {
+ if (p == null) {
+ return null;
+ }
+
+ final Map<String, String> values = new HashMap<String, String>();
+ for (final String name : m.attributes()) {
+ values.put(name, m.get(name));
+ }
+
+ String r = p.replace(values);
+ return r.isEmpty() ? null : r;
+ }
+
+ public AuthRequest authenticate(final AuthRequest who)
+ throws AccountException {
+ final String username = who.getLocalUser();
+ try {
+ final DirContext ctx = open();
+ try {
+ final LdapQuery.Result m = findAccount(ctx, username);
+
+ if (authConfig.getAuthType() == AuthType.LDAP) {
+ // We found the user account, but we need to verify
+ // the password matches it before we can continue.
+ //
+ authenticate(m.getDN(), who.getPassword());
+ }
+
+ who.setDisplayName(apply(accountFullName, m));
+ who.setSshUserName(apply(accountSshUserName, m));
+
+ if (accountEmailAddress != null) {
+ who.setEmailAddress(apply(accountEmailAddress, m));
+
+ } else if (emailExpander.canExpand(username)) {
+ // If LDAP cannot give us a valid email address for this user
+ // try expanding it through the older email expander code which
+ // assumes a user name within a domain.
+ //
+ who.setEmailAddress(emailExpander.expand(username));
+ }
+
+ // Fill the cache with the user's current groups. We've already
+ // spent the cost to open the LDAP connection, we might as well
+ // do one more call to get their group membership. Since we are
+ // in the middle of authenticating the user, its likely we will
+ // need to know what access rights they have soon.
+ //
+ membershipCache.put(username, queryForGroups(ctx, username, m));
+ return who;
+ } finally {
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ log.warn("Cannot close LDAP query handle", e);
+ }
+ }
+ } catch (NamingException e) {
+ log.error("Cannot query LDAP to autenticate user", e);
+ throw new AccountException("Cannot query LDAP for account", e);
+ }
+ }
+
+ @Override
+ public void onCreateAccount(final AuthRequest who, final Account account) {
+ usernameCache.put(who.getLocalUser(), account.getId());
+ }
+
+ @Override
+ public Set<AccountGroup.Id> groups(final AccountState who) {
+ final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>();
+ r.addAll(membershipCache.get(findId(who.getExternalIds())));
+ r.addAll(who.getInternalGroups());
+ return r;
+ }
+
+ private Set<AccountGroup.Id> queryForGroups(final String username)
+ throws NamingException, AccountException {
+ final DirContext ctx = open();
+ try {
+ return queryForGroups(ctx, username, null);
+ } finally {
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ log.warn("Cannot close LDAP query handle", e);
+ }
+ }
+ }
+
+ private Set<AccountGroup.Id> queryForGroups(final DirContext ctx,
+ final String username, LdapQuery.Result account) throws NamingException,
+ AccountException {
+ final Set<String> groupDNs = new HashSet<String>();
+
+ if (!groupMemberQueryList.isEmpty()) {
+ final HashMap<String, String> params = new HashMap<String, String>();
+
+ if (groupNeedsAccount) {
+ if (account == null) {
+ account = findAccount(ctx, username);
+ }
+ for (final String name : groupMemberQueryList.get(0).getParameters()) {
+ params.put(name, account.get(name));
+ }
+ }
+
+ params.put(USERNAME, username);
+
+ for (LdapQuery groupMemberQuery : groupMemberQueryList) {
+ for (LdapQuery.Result r : groupMemberQuery.query(ctx, params)) {
+ groupDNs.add(r.getDN());
+ }
+ }
+ }
+
+ if (accountMemberField != null) {
+ if (account == null) {
+ account = findAccount(ctx, username);
+ }
+
+ final Attribute groupAtt = account.getAll(accountMemberField);
+ if (groupAtt != null) {
+ final NamingEnumeration<?> groups = groupAtt.getAll();
+ while (groups.hasMore()) {
+ recursivelyExpandGroups(groupDNs, ctx, (String) groups.next());
+ }
+ }
+ }
+
+ final Set<AccountGroup.Id> actual = new HashSet<AccountGroup.Id>();
+ for (String dn : groupDNs) {
+ final AccountGroup group;
+
+ group = groupCache.get(new AccountGroup.ExternalNameKey(dn));
+ if (group != null && group.getType() == AccountGroup.Type.LDAP) {
+ actual.add(group.getId());
+ }
+ }
+
+ if (actual.isEmpty()) {
+ return Collections.emptySet();
+ } else {
+ return Collections.unmodifiableSet(actual);
+ }
+ }
+
+ private void recursivelyExpandGroups(final Set<String> groupDNs,
+ final DirContext ctx, final String groupDN) {
+ if (groupDNs.add(groupDN)) {
+ // Recursively identify the groups it is a member of.
+ //
+ try {
+ final Attribute in = ctx.getAttributes(groupDN).get(accountMemberField);
+ if (in != null) {
+ final NamingEnumeration<?> groups = in.getAll();
+ while (groups.hasMore()) {
+ recursivelyExpandGroups(groupDNs, ctx, (String) groups.next());
+ }
+ }
+ } catch (NamingException e) {
+ log.warn("Could not find group " + groupDN, e);
+ }
+ }
+ }
+
+ private static String findId(final Collection<AccountExternalId> ids) {
+ for (final AccountExternalId i : ids) {
+ if (i.isScheme(AccountExternalId.SCHEME_GERRIT)) {
+ return i.getSchemeRest(AccountExternalId.SCHEME_GERRIT);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Account.Id lookup(final String accountName) {
+ return usernameCache.get(accountName);
+ }
+
+ @Override
+ public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
+ final Set<AccountGroup.ExternalNameKey> out;
+ final ParamertizedString filter =
+ ParamertizedString.asis(groupPattern.replace(GROUPNAME, name)
+ .toString());
+ final Map<String, String> params = Collections.<String, String> emptyMap();
+
+ out = new HashSet<AccountGroup.ExternalNameKey>();
+ try {
+ final DirContext ctx = open();
+ try {
+ for (String groupBase : groupBases) {
+ final LdapQuery query =
+ new LdapQuery(groupBase, groupScope, filter, Collections
+ .<String> emptySet());
+ for (LdapQuery.Result res : query.query(ctx, params)) {
+ out.add(new AccountGroup.ExternalNameKey(res.getDN()));
+ }
+ }
+ } finally {
+ try {
+ ctx.close();
+ } catch (NamingException e) {
+ log.warn("Cannot close LDAP query handle", e);
+ }
+ }
+ } catch (NamingException e) {
+ log.warn("Cannot query LDAP for groups matching requested name", e);
+ }
+ return out;
+ }
+
+ private Account.Id queryForUsername(final String username) {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final String id = AccountExternalId.SCHEME_GERRIT + username;
+ final AccountExternalId extId =
+ db.accountExternalIds().get(new AccountExternalId.Key(id));
+ return extId != null ? extId.getAccountId() : null;
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ log.warn("Cannot query for username in database", e);
+ return null;
+ }
+ }
+
+ private Properties createContextProperties() {
+ final Properties env = new Properties();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP);
+ env.put(Context.PROVIDER_URL, server);
+ if (server.startsWith("ldaps:") && !sslVerify) {
+ Class<? extends SSLSocketFactory> factory = BlindSSLSocketFactory.class;
+ env.put("java.naming.ldap.factory.socket", factory.getName());
+ }
+ return env;
+ }
+
+ private DirContext open() throws NamingException {
+ final Properties env = createContextProperties();
+ if (username != null) {
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, username);
+ env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
+ }
+ return new InitialDirContext(env);
+ }
+
+ private void authenticate(String dn, String password) throws AccountException {
+ final Properties env = createContextProperties();
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, dn);
+ env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
+ try {
+ new InitialDirContext(env).close();
+ } catch (NamingException e) {
+ throw new AccountException("Incorrect username or password", e);
+ }
+ }
+
+ private LdapType discoverLdapType() {
+ try {
+ final DirContext ctx = open();
+ try {
+ return LdapType.guessType(ctx);
+ } finally {
+ ctx.close();
+ }
+ } catch (NamingException e) {
+ log.warn("Cannot discover type of LDAP server at " + server
+ + ", assuming the server is RFC 2307 compliant.", e);
+ return LdapType.RFC_2307;
+ }
+ }
+
+ private LdapQuery.Result findAccount(final DirContext ctx,
+ final String username) throws NamingException, AccountException {
+ final HashMap<String, String> params = new HashMap<String, String>();
+ params.put(USERNAME, username);
+
+ final List<LdapQuery.Result> res = new ArrayList<LdapQuery.Result>();
+ for (LdapQuery accountQuery : accountQueryList) {
+ res.addAll(accountQuery.query(ctx, params));
+ }
+
+ switch (res.size()) {
+ case 0:
+ throw new AccountException("No such user:" + username);
+
+ case 1:
+ return res.get(0);
+
+ default:
+ throw new AccountException("Duplicate users: " + username);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java
new file mode 100644
index 0000000000..4a0ea2cc49
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapType.java
@@ -0,0 +1,152 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.auth.ldap;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+abstract class LdapType {
+ static final LdapType RFC_2307 = new Rfc2307();
+
+ static LdapType guessType(final DirContext ctx) throws NamingException {
+ final SearchControls cons = new SearchControls();
+ final NamingEnumeration<SearchResult> res;
+
+ final Attributes rootAtts = ctx.getAttributes("");
+ Attribute supported = rootAtts.get("supportedCapabilities");
+ if (supported != null && supported.contains("1.2.840.113556.1.4.800")) {
+ return new ActiveDirectory(rootAtts);
+ }
+
+ return RFC_2307;
+ }
+
+ abstract String groupPattern();
+
+ abstract String groupMemberPattern();
+
+ abstract String accountFullName();
+
+ abstract String accountEmailAddress();
+
+ abstract String accountSshUserName();
+
+ abstract String accountMemberField();
+
+ abstract String accountPattern();
+
+ private static class Rfc2307 extends LdapType {
+ @Override
+ String groupPattern() {
+ return "(cn=${groupname})";
+ }
+
+ @Override
+ String groupMemberPattern() {
+ return "(memberUid=${username})";
+ }
+
+ @Override
+ String accountFullName() {
+ return "displayName";
+ }
+
+ @Override
+ String accountEmailAddress() {
+ return "mail";
+ }
+
+ @Override
+ String accountSshUserName() {
+ return "uid";
+ }
+
+ @Override
+ String accountMemberField() {
+ return null; // Not defined in RFC 2307
+ }
+
+ @Override
+ String accountPattern() {
+ return "(uid=${username})";
+ }
+ }
+
+ private static class ActiveDirectory extends LdapType {
+ private final String defaultDomain;
+
+ ActiveDirectory(final Attributes atts) throws NamingException {
+ // Convert "defaultNamingContext: DC=foo,DC=example,DC=com" into
+ // the a standard DNS name as we would expect to find in the suffix
+ // part of the userPrincipalName.
+ //
+ Attribute defaultNamingContext = atts.get("defaultNamingContext");
+ if (defaultNamingContext == null || defaultNamingContext.size() < 1) {
+ throw new NamingException("rootDSE has no defaultNamingContext");
+ }
+
+ final StringBuilder b = new StringBuilder();
+ for (String n : ((String) defaultNamingContext.get(0)).split(", *")) {
+ if (n.toUpperCase().startsWith("DC=")) {
+ if (b.length() > 0) {
+ b.append('.');
+ }
+ b.append(n.substring(3));
+ }
+ }
+ defaultDomain = b.toString();
+ }
+
+ @Override
+ String groupPattern() {
+ return "(&(objectClass=group)(cn=${groupname}))";
+ }
+
+ @Override
+ String groupMemberPattern() {
+ return null; // Active Directory uses memberOf in the account
+ }
+
+ @Override
+ String accountFullName() {
+ return "${givenName} ${sn}";
+ }
+
+ @Override
+ String accountEmailAddress() {
+ return "mail";
+ }
+
+ @Override
+ String accountSshUserName() {
+ return "${sAMAccountName.toLowerCase}";
+ }
+
+ @Override
+ String accountMemberField() {
+ return "memberOf";
+ }
+
+ @Override
+ String accountPattern() {
+ return "(&(objectClass=user)(sAMAccountName=${username}))";
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/SearchScope.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/SearchScope.java
new file mode 100644
index 0000000000..369914dc60
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/SearchScope.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.auth.ldap;
+
+import javax.naming.directory.SearchControls;
+
+public enum SearchScope {
+ // Search only the base DN
+ //
+ OBJECT(SearchControls.OBJECT_SCOPE), //
+ BASE(SearchControls.OBJECT_SCOPE),
+
+ // Search all entries one level under the base DN
+ //
+ // Does not include the base DN, and does not include items below items
+ // under the base DN.
+ //
+ ONE(SearchControls.ONELEVEL_SCOPE),
+
+ // Search all entries under the base DN, including the base DN.
+ //
+ SUBTREE(SearchControls.SUBTREE_SCOPE), //
+ SUB(SearchControls.SUBTREE_SCOPE);
+
+ private final int scope;
+
+ SearchScope(final int scope) {
+ this.scope = scope;
+ }
+
+ int scope() {
+ return scope;
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/cache/Cache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
index 86979b8483..86979b8483 100644
--- a/src/main/java/com/google/gerrit/server/cache/Cache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/Cache.java
diff --git a/src/main/java/com/google/gerrit/server/cache/CacheModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
index 23c094e626..23c094e626 100644
--- a/src/main/java/com/google/gerrit/server/cache/CacheModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheModule.java
diff --git a/src/main/java/com/google/gerrit/server/cache/CachePool.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
index cee10286e1..cee10286e1 100644
--- a/src/main/java/com/google/gerrit/server/cache/CachePool.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CachePool.java
diff --git a/src/main/java/com/google/gerrit/server/cache/CacheProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
index 6a7293ece0..6a7293ece0 100644
--- a/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/CacheProvider.java
diff --git a/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java
index cff4f111b5..cff4f111b5 100644
--- a/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/EvictionPolicy.java
diff --git a/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java
index d486425e78..d486425e78 100644
--- a/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/NamedCacheBinding.java
diff --git a/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java
index 8b3179a314..8b3179a314 100644
--- a/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/ProxyEhcache.java
diff --git a/src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java
index 2e0a9300a6..2e0a9300a6 100644
--- a/src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/SelfPopulatingCache.java
diff --git a/src/main/java/com/google/gerrit/server/cache/SimpleCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java
index d776412892..d776412892 100644
--- a/src/main/java/com/google/gerrit/server/cache/SimpleCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/SimpleCache.java
diff --git a/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java b/gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java
index 328ebd837e..328ebd837e 100644
--- a/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/cache/UnnamedCacheBinding.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
new file mode 100644
index 0000000000..25ab239fe9
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+class ApprovalTypesProvider implements Provider<ApprovalTypes> {
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ ApprovalTypesProvider(final SchemaFactory<ReviewDb> sf) {
+ schema = sf;
+ }
+
+ @Override
+ public ApprovalTypes get() {
+ List<ApprovalType> approvalTypes = new ArrayList<ApprovalType>(2);
+ List<ApprovalType> actionTypes = new ArrayList<ApprovalType>(2);
+
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ for (final ApprovalCategory c : db.approvalCategories().all()) {
+ final List<ApprovalCategoryValue> values =
+ db.approvalCategoryValues().byCategory(c.getId()).toList();
+ final ApprovalType type = new ApprovalType(c, values);
+ if (type.getCategory().isAction()) {
+ actionTypes.add(type);
+ } else {
+ approvalTypes.add(type);
+ }
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new ProvisionException("Cannot query approval categories", e);
+ }
+
+ approvalTypes = Collections.unmodifiableList(approvalTypes);
+ actionTypes = Collections.unmodifiableList(actionTypes);
+ return new ApprovalTypes(approvalTypes, actionTypes);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
new file mode 100644
index 0000000000..64fe6820e0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -0,0 +1,193 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gwtjsonrpc.server.SignedToken;
+import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Authentication related settings from {@code gerrit.config}. */
+@Singleton
+public class AuthConfig {
+ private final AuthType authType;
+ private final String httpHeader;
+ private final String logoutUrl;
+ private final String[] trusted;
+ private final SignedToken emailReg;
+
+ private final AccountGroup.Id administratorGroup;
+ private final Set<AccountGroup.Id> anonymousGroups;
+ private final Set<AccountGroup.Id> registeredGroups;
+
+ private final boolean allowGoogleAccountUpgrade;
+
+ @Inject
+ AuthConfig(@GerritServerConfig final Config cfg, final SystemConfig s)
+ throws XsrfException {
+ authType = toType(cfg);
+ httpHeader = cfg.getString("auth", null, "httpheader");
+ logoutUrl = cfg.getString("auth", null, "logouturl");
+ trusted = toTrusted(cfg);
+ emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey);
+
+ final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(2);
+ r.add(s.anonymousGroupId);
+ r.add(s.registeredGroupId);
+ registeredGroups = Collections.unmodifiableSet(r);
+ anonymousGroups = Collections.singleton(s.anonymousGroupId);
+ administratorGroup = s.adminGroupId;
+
+ if (authType == AuthType.OPENID) {
+ allowGoogleAccountUpgrade =
+ cfg.getBoolean("auth", "allowgoogleaccountupgrade", false);
+ } else {
+ allowGoogleAccountUpgrade = false;
+ }
+ }
+
+ private String[] toTrusted(final Config cfg) {
+ final String[] r = cfg.getStringList("auth", null, "trustedopenid");
+ if (r.length == 0) {
+ return new String[] {"http://", "https://"};
+ }
+ return r;
+ }
+
+ private static AuthType toType(final Config cfg) {
+ if (isBecomeAnyoneEnabled()) {
+ return AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
+ }
+ return ConfigUtil.getEnum(cfg, "auth", null, "type", AuthType.OPENID);
+ }
+
+ private static boolean isBecomeAnyoneEnabled() {
+ try {
+ String s = "com.google.gerrit.server.http.BecomeAnyAccountLoginServlet";
+ return Boolean.getBoolean(s);
+ } catch (SecurityException se) {
+ return false;
+ }
+ }
+
+ /** Type of user authentication used by this Gerrit server. */
+ public AuthType getAuthType() {
+ return authType;
+ }
+
+ public String getLoginHttpHeader() {
+ return httpHeader;
+ }
+
+ public String getLogoutURL() {
+ return logoutUrl;
+ }
+
+ public SignedToken getEmailRegistrationToken() {
+ return emailReg;
+ }
+
+ public boolean isAllowGoogleAccountUpgrade() {
+ return allowGoogleAccountUpgrade;
+ }
+
+ /** Identity of the magic group with full powers. */
+ public AccountGroup.Id getAdministratorsGroup() {
+ return administratorGroup;
+ }
+
+ /** Groups that all users, including anonymous users, belong to. */
+ public Set<AccountGroup.Id> getAnonymousGroups() {
+ return anonymousGroups;
+ }
+
+ /** Groups that all users who have created an account belong to. */
+ public Set<AccountGroup.Id> getRegisteredGroups() {
+ return registeredGroups;
+ }
+
+ public boolean isIdentityTrustable(final Collection<AccountExternalId> ids) {
+ switch (getAuthType()) {
+ case DEVELOPMENT_BECOME_ANY_ACCOUNT:
+ case HTTP:
+ case HTTP_LDAP:
+ case LDAP:
+ // Its safe to assume yes for an HTTP authentication type, as the
+ // only way in is through some external system that the admin trusts
+ //
+ return true;
+
+ case OPENID:
+ // All identities must be trusted in order to trust the account.
+ //
+ for (final AccountExternalId e : ids) {
+ if (!isTrusted(e)) {
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ // Assume not, we don't understand the login format.
+ //
+ return false;
+ }
+ }
+
+ private boolean isTrusted(final AccountExternalId id) {
+ if (id.isScheme(AccountExternalId.LEGACY_GAE)) {
+ // Assume this is a trusted token, its a legacy import from
+ // a fairly well respected provider and only takes effect if
+ // the administrator has the import still enabled
+ //
+ return isAllowGoogleAccountUpgrade();
+ }
+
+ if (id.isScheme(AccountExternalId.SCHEME_MAILTO)) {
+ // mailto identities are created by sending a unique validation
+ // token to the address and asking them to come back to the site
+ // with that token.
+ //
+ return true;
+ }
+
+ for (final String p : trusted) {
+ if (matches(p, id)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean matches(final String p, final AccountExternalId id) {
+ if (p.startsWith("^") && p.endsWith("$")) {
+ return id.getExternalId().matches(p);
+
+ } else {
+ return id.getExternalId().startsWith(p);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java
index e40c6a15d2..e40c6a15d2 100644
--- a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrl.java
diff --git a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java
index 1c70c78488..1c70c78488 100644
--- a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlModule.java
diff --git a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java
index 3cce4f0810..3cce4f0810 100644
--- a/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/CanonicalWebUrlProvider.java
diff --git a/src/main/java/com/google/gerrit/server/config/ConfigUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
index b507474105..b507474105 100644
--- a/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java
new file mode 100644
index 0000000000..659872a2be
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/DatabaseModule.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.jdbc.Database;
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
+
+import javax.sql.DataSource;
+
+/** Loads the database with standard dependencies. */
+public class DatabaseModule extends FactoryModule {
+ public static final Key<DataSource> DS =
+ Key.get(DataSource.class, Names.named("ReviewDb"));
+
+ @Override
+ protected void configure() {
+ bind(DS).toProvider(ReviewDbDataSourceProvider.class).in(SINGLETON);
+
+ bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).to(
+ new TypeLiteral<Database<ReviewDb>>() {}).in(SINGLETON);
+ bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(
+ ReviewDbDatabaseProvider.class).in(SINGLETON);
+
+ bind(SystemConfig.class).toProvider(SystemConfigProvider.class).in(
+ SINGLETON);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java
index a55ef84de5..a55ef84de5 100644
--- a/src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/EmailExpanderProvider.java
diff --git a/src/main/java/com/google/gerrit/server/config/FactoryModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java
index 331f4711cc..331f4711cc 100644
--- a/src/main/java/com/google/gerrit/server/config/FactoryModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/FactoryModule.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java
new file mode 100644
index 0000000000..a512654509
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.inject.AbstractModule;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.io.File;
+
+/** Injection for the really primitive configuration data. */
+public class GerritConfigModule extends AbstractModule {
+ @Override
+ protected void configure() {
+ bind(File.class).annotatedWith(SitePath.class).toProvider(
+ SitePathProvider.class).in(SINGLETON);
+ bind(Project.NameKey.class).annotatedWith(WildProjectName.class)
+ .toProvider(WildProjectNameProvider.class).in(SINGLETON);
+ bind(Config.class).annotatedWith(GerritServerConfig.class).toProvider(
+ GerritServerConfigProvider.class).in(SINGLETON);
+ bind(AuthConfig.class).in(SINGLETON);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
new file mode 100644
index 0000000000..406a772618
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -0,0 +1,163 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import static com.google.inject.Scopes.SINGLETON;
+import static com.google.inject.Stage.PRODUCTION;
+
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.AuthType;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.FileTypeRegistry;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.GerritPersonIdentProvider;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.MimeUtilFileTypeRegistry;
+import com.google.gerrit.server.ReplicationUser;
+import com.google.gerrit.server.account.AccountByEmailCacheImpl;
+import com.google.gerrit.server.account.AccountCacheImpl;
+import com.google.gerrit.server.account.AccountInfoCacheFactory;
+import com.google.gerrit.server.account.DefaultRealm;
+import com.google.gerrit.server.account.EmailExpander;
+import com.google.gerrit.server.account.GroupCacheImpl;
+import com.google.gerrit.server.account.Realm;
+import com.google.gerrit.server.auth.ldap.LdapModule;
+import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.server.git.ChangeMergeQueue;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MergeOp;
+import com.google.gerrit.server.git.MergeQueue;
+import com.google.gerrit.server.git.PatchSetImporter;
+import com.google.gerrit.server.git.PushAllProjectsOp;
+import com.google.gerrit.server.git.PushReplication;
+import com.google.gerrit.server.git.ReloadSubmitQueueOp;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.mail.AbandonedSender;
+import com.google.gerrit.server.mail.CommentSender;
+import com.google.gerrit.server.mail.EmailSender;
+import com.google.gerrit.server.mail.FromAddressGenerator;
+import com.google.gerrit.server.mail.FromAddressGeneratorProvider;
+import com.google.gerrit.server.mail.MergeFailSender;
+import com.google.gerrit.server.mail.MergedSender;
+import com.google.gerrit.server.mail.RegisterNewEmailSender;
+import com.google.gerrit.server.mail.SmtpEmailSender;
+import com.google.gerrit.server.patch.PatchListCacheImpl;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.project.ProjectCacheImpl;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Starts global state with standard dependencies. */
+public class GerritGlobalModule extends FactoryModule {
+ public static Injector createInjector() {
+ final Injector db = Guice.createInjector(PRODUCTION, new DatabaseModule());
+ final CanonicalWebUrlModule canonicalWebUrl = new CanonicalWebUrlModule() {
+ @Override
+ protected Class<? extends Provider<String>> provider() {
+ return CanonicalWebUrlProvider.class;
+ }
+ };
+ return createInjector(db, canonicalWebUrl);
+ }
+
+ public static Injector createInjector(final Injector db,
+ final CanonicalWebUrlModule canonicalWebUrl) {
+ final Injector cfg = db.createChildInjector(new GerritConfigModule());
+ final List<Module> modules = new ArrayList<Module>();
+ modules.add(cfg.getInstance(GerritGlobalModule.class));
+ modules.add(canonicalWebUrl);
+ return cfg.createChildInjector(modules);
+ }
+
+ private final AuthType loginType;
+
+ @Inject
+ GerritGlobalModule(final AuthConfig authConfig,
+ @GerritServerConfig final Config config) {
+ loginType = authConfig.getAuthType();
+ }
+
+ @Override
+ protected void configure() {
+ switch (loginType) {
+ case HTTP_LDAP:
+ case LDAP:
+ install(new LdapModule());
+ break;
+
+ default:
+ bind(Realm.class).to(DefaultRealm.class);
+ break;
+ }
+
+ bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in(
+ SINGLETON);
+ bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in(
+ SINGLETON);
+ bind(AnonymousUser.class);
+
+ bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
+ GerritPersonIdentProvider.class);
+
+ bind(CachePool.class);
+ install(AccountByEmailCacheImpl.module());
+ install(AccountCacheImpl.module());
+ install(GroupCacheImpl.module());
+ install(PatchListCacheImpl.module());
+ install(ProjectCacheImpl.module());
+
+ factory(AccountInfoCacheFactory.Factory.class);
+ factory(ProjectState.Factory.class);
+
+ bind(GitRepositoryManager.class);
+ bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
+ bind(WorkQueue.class);
+
+ bind(ReplicationQueue.class).to(PushReplication.class).in(SINGLETON);
+ factory(PushAllProjectsOp.Factory.class);
+
+ bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
+ factory(MergeOp.Factory.class);
+ factory(ReloadSubmitQueueOp.Factory.class);
+
+ bind(FromAddressGenerator.class).toProvider(
+ FromAddressGeneratorProvider.class).in(SINGLETON);
+ bind(EmailSender.class).to(SmtpEmailSender.class).in(SINGLETON);
+
+ factory(PatchSetImporter.Factory.class);
+ bind(PatchSetInfoFactory.class);
+ bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
+ factory(FunctionState.Factory.class);
+
+ factory(AbandonedSender.Factory.class);
+ factory(CommentSender.Factory.class);
+ factory(MergedSender.Factory.class);
+ factory(MergeFailSender.Factory.class);
+ factory(RegisterNewEmailSender.Factory.class);
+ factory(ReplicationUser.Factory.class);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
new file mode 100644
index 0000000000..fa6e0149df
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RequestCleanup;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.GroupControl;
+import com.google.gerrit.server.mail.AddReviewerSender;
+import com.google.gerrit.server.mail.CreateChangeSender;
+import com.google.gerrit.server.mail.ReplacePatchSetSender;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.inject.servlet.RequestScoped;
+
+/** Bindings for {@link RequestScoped} entities. */
+public class GerritRequestModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ bind(RequestCleanup.class).in(RequestScoped.class);
+ bind(ReviewDb.class).toProvider(RequestScopedReviewDbProvider.class).in(
+ RequestScoped.class);
+ bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
+ bind(AccountResolver.class);
+
+ bind(ChangeControl.Factory.class).in(SINGLETON);
+ bind(GroupControl.Factory.class).in(SINGLETON);
+ bind(ProjectControl.Factory.class).in(SINGLETON);
+
+ // Not really per-request, but dammit, I don't know where else to
+ // easily park this stuff.
+ //
+ factory(AddReviewerSender.Factory.class);
+ factory(CreateChangeSender.Factory.class);
+ factory(ReplacePatchSetSender.Factory.class);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/config/GerritServerConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfig.java
index 0dc1e683b8..0dc1e683b8 100644
--- a/src/main/java/com/google/gerrit/server/config/GerritServerConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfig.java
diff --git a/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
index a7d134deee..a7d134deee 100644
--- a/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritServerConfigProvider.java
diff --git a/src/main/java/com/google/gerrit/server/config/Nullable.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/Nullable.java
index eb7ae0bb81..eb7ae0bb81 100644
--- a/src/main/java/com/google/gerrit/server/config/Nullable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/Nullable.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
new file mode 100644
index 0000000000..5aa78cbb2d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.RequestCleanup;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.jdbc.Database;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+
+/** Provides {@link ReviewDb} database handle live only for this request. */
+@Singleton
+final class RequestScopedReviewDbProvider implements Provider<ReviewDb> {
+ private final Database<ReviewDb> schema;
+ private final Provider<RequestCleanup> cleanup;
+
+ @Inject
+ RequestScopedReviewDbProvider(final Database<ReviewDb> schema,
+ final Provider<RequestCleanup> cleanup) {
+ this.schema = schema;
+ this.cleanup = cleanup;
+ }
+
+ @Override
+ public ReviewDb get() {
+ final ReviewDb c;
+ try {
+ c = schema.open();
+ } catch (OrmException e) {
+ throw new ProvisionException("Cannot open ReviewDb", e);
+ }
+ try {
+ cleanup.get().add(new Runnable() {
+ @Override
+ public void run() {
+ c.close();
+ }
+ });
+ return c;
+ } catch (Error e) {
+ c.close();
+ throw e;
+ } catch (RuntimeException e) {
+ c.close();
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java
index eed571aa70..eed571aa70 100644
--- a/src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDataSourceProvider.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java
new file mode 100644
index 0000000000..e5b7e8119a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.jdbc.Database;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.name.Named;
+
+import javax.sql.DataSource;
+
+/** Provides the {@code Database<ReviewDb>} database handle. */
+final class ReviewDbDatabaseProvider implements Provider<Database<ReviewDb>> {
+ private final DataSource datasource;
+
+ @Inject
+ ReviewDbDatabaseProvider(@Named("ReviewDb") final DataSource ds) {
+ datasource = ds;
+ }
+
+ @Override
+ public Database<ReviewDb> get() {
+ try {
+ return new Database<ReviewDb>(datasource, ReviewDb.class);
+ } catch (OrmException e) {
+ throw new ProvisionException("Cannot create ReviewDb", e);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/config/SitePath.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePath.java
index d0b3905252..d0b3905252 100644
--- a/src/main/java/com/google/gerrit/server/config/SitePath.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePath.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathProvider.java
new file mode 100644
index 0000000000..f9fb021c84
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathProvider.java
@@ -0,0 +1,37 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.io.File;
+
+/** Provides {@link java.io.File} annotated with {@link SitePath}. */
+public class SitePathProvider implements Provider<File> {
+ private final File path;
+
+ @Inject
+ SitePathProvider(final SystemConfig config) {
+ final String p = config.sitePath;
+ path = new File(p != null && p.length() > 0 ? p : ".").getAbsoluteFile();
+ }
+
+ @Override
+ public File get() {
+ return path;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java
new file mode 100644
index 0000000000..5621b4fb09
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java
@@ -0,0 +1,338 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SchemaVersion;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.workflow.NoOpFunction;
+import com.google.gerrit.server.workflow.SubmitFunction;
+import com.google.gwtjsonrpc.server.SignedToken;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Loads the {@link SystemConfig} from the database. */
+public class SystemConfigProvider implements Provider<SystemConfig> {
+ private static final Project.NameKey DEFAULT_WILD_NAME =
+ new Project.NameKey("-- All Projects --");
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ public SystemConfigProvider(final SchemaFactory<ReviewDb> sf) {
+ schema = sf;
+ }
+
+ @Override
+ public SystemConfig get() {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ SchemaVersion sVer = getSchemaVersion(db);
+
+ if (sVer == null) {
+ // Assume the schema is empty and try to populate it.
+ //
+ sVer = createSchema(db);
+ }
+
+ switch (sVer.versionNbr) {
+ case 2:
+ initPushTagCategory(db);
+ initPushUpdateBranchCategory(db);
+
+ sVer.versionNbr = 3;
+ db.schemaVersion().update(Collections.singleton(sVer));
+ break;
+
+ case 15:
+ sVer.versionNbr = 16;
+ db.schemaVersion().update(Collections.singleton(sVer));
+ break;
+ }
+
+ if (sVer.versionNbr != ReviewDb.VERSION) {
+ throw new OrmException("Unsupported schema version "
+ + sVer.versionNbr + "; expected schema version "
+ + ReviewDb.VERSION);
+ }
+
+ final List<SystemConfig> all = db.systemConfig().all().toList();
+ switch (all.size()) {
+ case 1:
+ return all.get(0);
+ case 0:
+ throw new OrmException("system_config table is empty");
+ default:
+ throw new OrmException("system_config must have exactly 1 row;"
+ + " found " + all.size() + " rows instead");
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new ProvisionException("Cannot read system_config", e);
+ }
+ }
+
+ private SchemaVersion createSchema(final ReviewDb db) throws OrmException {
+ db.createSchema();
+
+ final SchemaVersion sVer = SchemaVersion.create();
+ sVer.versionNbr = ReviewDb.VERSION;
+ db.schemaVersion().insert(Collections.singleton(sVer));
+
+ final SystemConfig sConfig = initSystemConfig(db);
+ initOwnerCategory(db);
+ initReadCategory(db, sConfig);
+ initVerifiedCategory(db);
+ initCodeReviewCategory(db, sConfig);
+ initSubmitCategory(db);
+ initPushTagCategory(db);
+ initPushUpdateBranchCategory(db);
+ initWildCardProject(db);
+
+ return sVer;
+ }
+
+ private SchemaVersion getSchemaVersion(final ReviewDb db) {
+ try {
+ return db.schemaVersion().get(new SchemaVersion.Key());
+ } catch (OrmException e) {
+ return null;
+ }
+ }
+
+ private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
+ final AccountGroup admin =
+ new AccountGroup(new AccountGroup.NameKey("Administrators"),
+ new AccountGroup.Id(c.nextAccountGroupId()));
+ admin.setDescription("Gerrit Site Administrators");
+ admin.setType(AccountGroup.Type.INTERNAL);
+ c.accountGroups().insert(Collections.singleton(admin));
+
+ final AccountGroup anonymous =
+ new AccountGroup(new AccountGroup.NameKey("Anonymous Users"),
+ new AccountGroup.Id(c.nextAccountGroupId()));
+ anonymous.setDescription("Any user, signed-in or not");
+ anonymous.setOwnerGroupId(admin.getId());
+ anonymous.setType(AccountGroup.Type.SYSTEM);
+ c.accountGroups().insert(Collections.singleton(anonymous));
+
+ final AccountGroup registered =
+ new AccountGroup(new AccountGroup.NameKey("Registered Users"),
+ new AccountGroup.Id(c.nextAccountGroupId()));
+ registered.setDescription("Any signed-in user");
+ registered.setOwnerGroupId(admin.getId());
+ registered.setType(AccountGroup.Type.SYSTEM);
+ c.accountGroups().insert(Collections.singleton(registered));
+
+ File sitePath = new File(".").getAbsoluteFile();
+ if (".".equals(sitePath.getName())) {
+ sitePath = sitePath.getParentFile();
+ }
+
+ final SystemConfig s = SystemConfig.create();
+ s.registerEmailPrivateKey = SignedToken.generateRandomKey();
+ s.adminGroupId = admin.getId();
+ s.anonymousGroupId = anonymous.getId();
+ s.registeredGroupId = registered.getId();
+ s.sitePath = sitePath.getAbsolutePath();
+ c.systemConfig().insert(Collections.singleton(s));
+ return s;
+ }
+
+ private void initWildCardProject(final ReviewDb c) throws OrmException {
+ final Project p;
+
+ p = new Project(DEFAULT_WILD_NAME, WildProjectNameProvider.WILD_PROJECT_ID);
+ p.setDescription("Rights inherited by all other projects");
+ p.setUseContributorAgreements(false);
+ c.projects().insert(Collections.singleton(p));
+ }
+
+ private void initVerifiedCategory(final ReviewDb c) throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(new ApprovalCategory.Id("VRIF"), "Verified");
+ cat.setPosition((short) 0);
+ cat.setAbbreviatedName("V");
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, 1, "Verified"));
+ vals.add(value(cat, 0, "No score"));
+ vals.add(value(cat, -1, "Fails"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ }
+
+ private void initCodeReviewCategory(final ReviewDb c,
+ final SystemConfig sConfig) throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(new ApprovalCategory.Id("CRVW"), "Code Review");
+ cat.setPosition((short) 1);
+ cat.setAbbreviatedName("R");
+ cat.setCopyMinScore(true);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, 2, "Looks good to me, approved"));
+ vals.add(value(cat, 1, "Looks good to me, but someone else must approve"));
+ vals.add(value(cat, 0, "No score"));
+ vals.add(value(cat, -1, "I would prefer that you didn't submit this"));
+ vals.add(value(cat, -2, "Do not submit"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+
+ final ProjectRight approve =
+ new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
+ sConfig.registeredGroupId));
+ approve.setMaxValue((short) 1);
+ approve.setMinValue((short) -1);
+ c.projectRights().insert(Collections.singleton(approve));
+ }
+
+ private void initOwnerCategory(final ReviewDb c) throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner");
+ cat.setPosition((short) -1);
+ cat.setFunctionName(NoOpFunction.NAME);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, 1, "Administer All Settings"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ }
+
+ private void initReadCategory(final ReviewDb c, final SystemConfig sConfig)
+ throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access");
+ cat.setPosition((short) -1);
+ cat.setFunctionName(NoOpFunction.NAME);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, 2, "Upload permission"));
+ vals.add(value(cat, 1, "Read access"));
+ vals.add(value(cat, -1, "No access"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ {
+ final ProjectRight read =
+ new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
+ sConfig.anonymousGroupId));
+ read.setMaxValue((short) 1);
+ read.setMinValue((short) 1);
+ c.projectRights().insert(Collections.singleton(read));
+ }
+ {
+ final ProjectRight read =
+ new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
+ sConfig.registeredGroupId));
+ read.setMaxValue((short) 2);
+ read.setMinValue((short) 1);
+ c.projectRights().insert(Collections.singleton(read));
+ }
+ {
+ final ProjectRight read =
+ new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
+ sConfig.adminGroupId));
+ read.setMaxValue((short) 1);
+ read.setMinValue((short) 1);
+ c.projectRights().insert(Collections.singleton(read));
+ }
+ }
+
+ private void initSubmitCategory(final ReviewDb c) throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit");
+ cat.setPosition((short) -1);
+ cat.setFunctionName(SubmitFunction.NAME);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, 1, "Submit"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ }
+
+ private void initPushTagCategory(final ReviewDb c) throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Annotated Tag");
+ cat.setPosition((short) -1);
+ cat.setFunctionName(NoOpFunction.NAME);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag"));
+ vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED,
+ "Create Annotated Tag"));
+ vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANY, "Create Any Tag"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ }
+
+ private void initPushUpdateBranchCategory(final ReviewDb c)
+ throws OrmException {
+ final Transaction txn = c.beginTransaction();
+ final ApprovalCategory cat;
+ final ArrayList<ApprovalCategoryValue> vals;
+
+ cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch");
+ cat.setPosition((short) -1);
+ cat.setFunctionName(NoOpFunction.NAME);
+ vals = new ArrayList<ApprovalCategoryValue>();
+ vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch"));
+ vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch"));
+ vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE,
+ "Force Push Branch; Delete Branch"));
+ c.approvalCategories().insert(Collections.singleton(cat), txn);
+ c.approvalCategoryValues().insert(vals, txn);
+ txn.commit();
+ }
+
+ private static ApprovalCategoryValue value(final ApprovalCategory cat,
+ final int value, final String name) {
+ return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(),
+ (short) value), name);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectName.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectName.java
new file mode 100644
index 0000000000..3df550aabd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectName.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Marker on a {@link Project.NameKey} for the current wildcard project.
+ * <p>
+ * This is the name of the project whose rights inherit into every other project
+ * in the system.
+ */
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface WildProjectName {
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
new file mode 100644
index 0000000000..bf35ca8beb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
@@ -0,0 +1,45 @@
+package com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+class WildProjectNameProvider implements Provider<Project.NameKey> {
+ /** Project.Id meaning "any and all projects on this server". */
+ static final Project.Id WILD_PROJECT_ID = new Project.Id(0);
+
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ WildProjectNameProvider(final SchemaFactory<ReviewDb> schema,
+ /*
+ * Unused, but we need to force it to load before we do, otherwise we risk
+ * reading an empty database without the wild project being in the database.
+ * Asking for it should ensures Guice loads it first.
+ */
+ final SystemConfig config) {
+ this.schema = schema;
+ }
+
+ public Project.NameKey get() {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final Project p = db.projects().get(WILD_PROJECT_ID);
+ if (p == null) {
+ throw new ProvisionException("No project " + WILD_PROJECT_ID);
+ }
+ return p.getNameKey();
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new ProvisionException("Cannot load " + WILD_PROJECT_ID, e);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java
new file mode 100644
index 0000000000..84c7c75eae
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStore.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.contact;
+
+import com.google.gerrit.common.errors.ContactInformationStoreException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ContactInformation;
+
+public interface ContactStore {
+ boolean isEnabled();
+
+ void store(Account account, ContactInformation info)
+ throws ContactInformationStoreException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java
new file mode 100644
index 0000000000..9ad4a716f1
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java
@@ -0,0 +1,84 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.contact;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePath;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.eclipse.jgit.lib.Config;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/** Creates the {@link ContactStore} based on the configuration. */
+public class ContactStoreProvider implements Provider<ContactStore> {
+ private final Config config;
+ private final File sitePath;
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ ContactStoreProvider(@GerritServerConfig final Config config,
+ @SitePath final File sitePath, final SchemaFactory<ReviewDb> schema) {
+ this.config = config;
+ this.sitePath = sitePath;
+ this.schema = schema;
+ }
+
+ @Override
+ public ContactStore get() {
+ final String url = config.getString("contactstore", null, "url");
+ if (url == null) {
+ return new NoContactStore();
+ }
+
+ if (!havePGP()) {
+ throw new ProvisionException("BouncyCastle PGP not installed; "
+ + " needed to encrypt contact information");
+ }
+
+ final URL storeUrl;
+ try {
+ storeUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ throw new ProvisionException("Invalid contactstore.url: " + url, e);
+ }
+
+ final String storeAPPSEC = config.getString("contactstore", null, "appsec");
+ final File pubkey = new File(sitePath, "contact_information.pub");
+ if (!pubkey.exists()) {
+ throw new ProvisionException("PGP public key file \""
+ + pubkey.getAbsolutePath() + "\" not found");
+ }
+ return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema);
+ }
+
+ private static boolean havePGP() {
+ try {
+ Class.forName(PGPPublicKey.class.getName());
+ return true;
+ } catch (NoClassDefFoundError noBouncyCastle) {
+ return false;
+ } catch (ClassNotFoundException noBouncyCastle) {
+ return false;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
new file mode 100644
index 0000000000..bd79dc4906
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
@@ -0,0 +1,311 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.contact;
+
+import com.google.gerrit.common.errors.ContactInformationStoreException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ContactInformation;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.UrlEncoded;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.openpgp.PGPCompressedData;
+import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
+import org.bouncycastle.openpgp.PGPEncryptedData;
+import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPLiteralData;
+import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.util.NB;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.TimeZone;
+
+/** Encrypts {@link ContactInformation} instances and saves them. */
+@Singleton
+class EncryptedContactStore implements ContactStore {
+ private static final Logger log =
+ LoggerFactory.getLogger(EncryptedContactStore.class);
+ private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final PGPPublicKey dest;
+ private final SecureRandom prng;
+ private final URL storeUrl;
+ private final String storeAPPSEC;
+
+ EncryptedContactStore(final URL storeUrl, final String storeAPPSEC,
+ final File pubKey, final SchemaFactory<ReviewDb> schema) {
+ this.storeUrl = storeUrl;
+ this.storeAPPSEC = storeAPPSEC;
+ this.schema = schema;
+ this.dest = selectKey(readPubRing(pubKey));
+
+ final String prngName = "SHA1PRNG";
+ try {
+ prng = SecureRandom.getInstance(prngName);
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProvisionException("Cannot create " + prngName, e);
+ }
+
+ // Run a test encryption to verify the proper algorithms exist in
+ // our JVM and we are able to invoke them. This helps to identify
+ // a system configuration problem early at startup, rather than a
+ // lot later during runtime.
+ //
+ try {
+ encrypt("test", new Date(0), "test".getBytes("UTF-8"));
+ } catch (NoSuchProviderException e) {
+ throw new ProvisionException("PGP encryption not available", e);
+ } catch (PGPException e) {
+ throw new ProvisionException("PGP encryption not available", e);
+ } catch (IOException e) {
+ throw new ProvisionException("PGP encryption not available", e);
+ }
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ private static PGPPublicKeyRingCollection readPubRing(final File pub) {
+ try {
+ InputStream in = new FileInputStream(pub);
+ try {
+ in = PGPUtil.getDecoderStream(in);
+ return new PGPPublicKeyRingCollection(in);
+ } finally {
+ in.close();
+ }
+ } catch (IOException e) {
+ throw new ProvisionException("Cannot read " + pub, e);
+ } catch (PGPException e) {
+ throw new ProvisionException("Cannot read " + pub, e);
+ }
+ }
+
+ private static PGPPublicKey selectKey(final PGPPublicKeyRingCollection rings) {
+ for (final Iterator<?> ri = rings.getKeyRings(); ri.hasNext();) {
+ final PGPPublicKeyRing currRing = (PGPPublicKeyRing) ri.next();
+ for (final Iterator<?> ki = currRing.getPublicKeys(); ki.hasNext();) {
+ final PGPPublicKey k = (PGPPublicKey) ki.next();
+ if (k.isEncryptionKey()) {
+ return k;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void store(final Account account, final ContactInformation info)
+ throws ContactInformationStoreException {
+ try {
+ final byte[] plainText = format(account, info).getBytes("UTF-8");
+ final String fileName = "account-" + account.getId();
+ final Date fileDate = account.getContactFiledOn();
+ final byte[] encText = encrypt(fileName, fileDate, plainText);
+ final String encStr = new String(encText, "UTF-8");
+
+ final Timestamp filedOn = account.getContactFiledOn();
+ final UrlEncoded u = new UrlEncoded();
+ if (storeAPPSEC != null) {
+ u.put("APPSEC", storeAPPSEC);
+ }
+ if (account.getPreferredEmail() != null) {
+ u.put("email", account.getPreferredEmail());
+ }
+ if (filedOn != null) {
+ u.put("filed", String.valueOf(filedOn.getTime() / 1000L));
+ }
+ u.put("account_id", String.valueOf(account.getId().get()));
+ u.put("data", encStr);
+ final byte[] body = u.toString().getBytes("UTF-8");
+
+ final HttpURLConnection c = (HttpURLConnection) storeUrl.openConnection();
+ c.setRequestMethod("POST");
+ c.setRequestProperty("Content-Type",
+ "application/x-www-form-urlencoded; charset=UTF-8");
+ c.setDoOutput(true);
+ c.setFixedLengthStreamingMode(body.length);
+ final OutputStream out = c.getOutputStream();
+ out.write(body);
+ out.close();
+
+ if (c.getResponseCode() == 200) {
+ final byte[] dst = new byte[2];
+ final InputStream in = c.getInputStream();
+ try {
+ NB.readFully(in, dst, 0, 2);
+ } finally {
+ in.close();
+ }
+ if (dst[0] != 'O' || dst[1] != 'K') {
+ throw new IOException("Store failed: " + c.getResponseCode());
+ }
+ } else {
+ throw new IOException("Store failed: " + c.getResponseCode());
+ }
+
+ } catch (IOException e) {
+ log.error("Cannot store encrypted contact information", e);
+ throw new ContactInformationStoreException(e);
+ } catch (PGPException e) {
+ log.error("Cannot store encrypted contact information", e);
+ throw new ContactInformationStoreException(e);
+ } catch (NoSuchProviderException e) {
+ log.error("Cannot store encrypted contact information", e);
+ throw new ContactInformationStoreException(e);
+ }
+ }
+
+ private byte[] encrypt(final String name, final Date date,
+ final byte[] rawText) throws NoSuchProviderException, PGPException,
+ IOException {
+ final byte[] zText = compress(name, date, rawText);
+ final PGPEncryptedDataGenerator cpk =
+ new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, prng, "BC");
+ cpk.addMethod(dest);
+
+ final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ final ArmoredOutputStream aout = new ArmoredOutputStream(buf);
+ final OutputStream cout = cpk.open(aout, zText.length);
+ cout.write(zText);
+ cout.close();
+ aout.close();
+
+ return buf.toByteArray();
+ }
+
+ private static byte[] compress(final String fileName, Date fileDate,
+ final byte[] plainText) throws IOException {
+ final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ final PGPCompressedDataGenerator comdg;
+ final int len = plainText.length;
+ if (fileDate == null) {
+ fileDate = PGPLiteralData.NOW;
+ }
+
+ comdg = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
+ final OutputStream out =
+ new PGPLiteralDataGenerator().open(comdg.open(buf),
+ PGPLiteralData.BINARY, fileName, len, fileDate);
+ out.write(plainText);
+ out.close();
+ comdg.close();
+ return buf.toByteArray();
+ }
+
+ private String format(final Account account, final ContactInformation info)
+ throws ContactInformationStoreException {
+ Timestamp on = account.getContactFiledOn();
+ if (on == null) {
+ on = new Timestamp(System.currentTimeMillis());
+ }
+
+ final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ df.setTimeZone(UTC);
+
+ final StringBuilder b = new StringBuilder();
+ field(b, "Account-Id", account.getId().toString());
+ field(b, "Date", df.format(on) + " " + UTC.getID());
+ field(b, "Full-Name", account.getFullName());
+ field(b, "Preferred-Email", account.getPreferredEmail());
+
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ for (final AccountExternalId e : db.accountExternalIds().byAccount(
+ account.getId())) {
+ final StringBuilder oistr = new StringBuilder();
+ if (e.getEmailAddress() != null && e.getEmailAddress().length() > 0) {
+ if (oistr.length() > 0) {
+ oistr.append(' ');
+ }
+ oistr.append(e.getEmailAddress());
+ }
+ if (e.isScheme(AccountExternalId.SCHEME_MAILTO)) {
+ if (oistr.length() > 0) {
+ oistr.append(' ');
+ }
+ oistr.append('<');
+ oistr.append(e.getExternalId());
+ oistr.append('>');
+ }
+ field(b, "Identity", oistr.toString());
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ throw new ContactInformationStoreException(e);
+ }
+
+ field(b, "Address", info.getAddress());
+ field(b, "Country", info.getCountry());
+ field(b, "Phone-Number", info.getPhoneNumber());
+ field(b, "Fax-Number", info.getFaxNumber());
+ return b.toString();
+ }
+
+ private static void field(final StringBuilder b, final String name,
+ String value) {
+ if (value == null) {
+ return;
+ }
+ value = value.trim();
+ if (value.length() == 0) {
+ return;
+ }
+
+ b.append(name);
+ b.append(':');
+ if (value.indexOf('\n') == -1) {
+ b.append(' ');
+ b.append(value);
+ } else {
+ value = value.replaceAll("\r\n", "\n");
+ value = value.replaceAll("\n", "\n\t");
+ b.append("\n\t");
+ b.append(value);
+ }
+ b.append('\n');
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
new file mode 100644
index 0000000000..e219186446
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.contact;
+
+import com.google.gerrit.common.errors.ContactInformationStoreException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ContactInformation;
+
+class NoContactStore implements ContactStore {
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+
+ @Override
+ public void store(Account account, ContactInformation info)
+ throws ContactInformationStoreException {
+ throw new ContactInformationStoreException(new IllegalStateException(
+ "ContactStore not configured"));
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
new file mode 100644
index 0000000000..d6c8f4ab3b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeMergeQueue.java
@@ -0,0 +1,197 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class ChangeMergeQueue implements MergeQueue {
+ private static final Logger log =
+ LoggerFactory.getLogger(ChangeMergeQueue.class);
+
+ private final Map<Branch.NameKey, MergeEntry> active =
+ new HashMap<Branch.NameKey, MergeEntry>();
+ private final Map<Branch.NameKey, RecheckJob> recheck =
+ new HashMap<Branch.NameKey, RecheckJob>();
+
+ private final WorkQueue workQueue;
+ private final MergeOp.Factory opFactory;
+
+ @Inject
+ ChangeMergeQueue(final WorkQueue wq, final MergeOp.Factory of) {
+ workQueue = wq;
+ opFactory = of;
+ }
+
+ @Override
+ public void merge(final Branch.NameKey branch) {
+ if (start(branch)) {
+ mergeImpl(branch);
+ }
+ }
+
+ private synchronized boolean start(final Branch.NameKey branch) {
+ final MergeEntry e = active.get(branch);
+ if (e == null) {
+ // Let the caller attempt this merge, its the only one interested
+ // in processing this branch right now.
+ //
+ active.put(branch, new MergeEntry(branch));
+ return true;
+ } else {
+ // Request that the job queue handle this merge later.
+ //
+ e.needMerge = true;
+ return false;
+ }
+ }
+
+ @Override
+ public synchronized void schedule(final Branch.NameKey branch) {
+ MergeEntry e = active.get(branch);
+ if (e == null) {
+ e = new MergeEntry(branch);
+ active.put(branch, e);
+ }
+ e.needMerge = true;
+ scheduleJob(e);
+ }
+
+ @Override
+ public synchronized void recheckAfter(final Branch.NameKey branch,
+ final long delay, final TimeUnit delayUnit) {
+ final long now = System.currentTimeMillis();
+ final long at = now + MILLISECONDS.convert(delay, delayUnit);
+ RecheckJob e = recheck.get(branch);
+ if (e == null) {
+ e = new RecheckJob(branch);
+ workQueue.getDefaultQueue().schedule(e, now - at, MILLISECONDS);
+ recheck.put(branch, e);
+ }
+ e.recheckAt = Math.max(at, e.recheckAt);
+ }
+
+ private synchronized void finish(final Branch.NameKey branch) {
+ final MergeEntry e = active.get(branch);
+ if (e == null) {
+ // Not registered? Shouldn't happen but ignore it.
+ //
+ return;
+ }
+
+ if (!e.needMerge) {
+ // No additional merges are in progress, we can delete it.
+ //
+ active.remove(branch);
+ return;
+ }
+
+ scheduleJob(e);
+ }
+
+ private void scheduleJob(final MergeEntry e) {
+ if (!e.jobScheduled) {
+ // No job has been scheduled to execute this branch, but it needs
+ // to run a merge again.
+ //
+ e.jobScheduled = true;
+ workQueue.getDefaultQueue().schedule(e, 0, TimeUnit.SECONDS);
+ }
+ }
+
+ private synchronized void unschedule(final MergeEntry e) {
+ e.jobScheduled = false;
+ e.needMerge = false;
+ }
+
+ private void mergeImpl(final Branch.NameKey branch) {
+ try {
+ opFactory.create(branch).merge();
+ } catch (Throwable e) {
+ log.error("Merge attempt for " + branch + " failed", e);
+ } finally {
+ finish(branch);
+ }
+ }
+
+ private synchronized void recheck(final RecheckJob e) {
+ final long remainingDelay = e.recheckAt - System.currentTimeMillis();
+ if (MILLISECONDS.convert(10, SECONDS) < remainingDelay) {
+ // Woke up too early, the job deadline was pushed back.
+ // Reschedule for the new deadline. We allow for a small
+ // amount of fuzz due to multiple reschedule attempts in
+ // a short period of time being caused by MergeOp.
+ //
+ workQueue.getDefaultQueue().schedule(e, remainingDelay, MILLISECONDS);
+ } else {
+ // Schedule a merge attempt on this branch to see if we can
+ // actually complete it this time.
+ //
+ schedule(e.dest);
+ }
+ }
+
+ private class MergeEntry implements Runnable {
+ final Branch.NameKey dest;
+ boolean needMerge;
+ boolean jobScheduled;
+
+ MergeEntry(final Branch.NameKey d) {
+ dest = d;
+ }
+
+ public void run() {
+ unschedule(this);
+ mergeImpl(dest);
+ }
+
+ @Override
+ public String toString() {
+ final Project.NameKey project = dest.getParentKey();
+ return "submit " + project.get() + " " + dest.getShortName();
+ }
+ }
+
+ private class RecheckJob implements Runnable {
+ final Branch.NameKey dest;
+ long recheckAt;
+
+ RecheckJob(final Branch.NameKey d) {
+ dest = d;
+ }
+
+ @Override
+ public void run() {
+ recheck(this);
+ }
+
+ @Override
+ public String toString() {
+ final Project.NameKey project = dest.getParentKey();
+ return "recheck " + project.get() + " " + dest.getShortName();
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CodeReviewCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CodeReviewCommit.java
new file mode 100644
index 0000000000..863c0bd80f
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CodeReviewCommit.java
@@ -0,0 +1,73 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import java.util.List;
+
+/** Extended commit entity with code review specific metadata. */
+class CodeReviewCommit extends RevCommit {
+ static CodeReviewCommit error(final CommitMergeStatus s) {
+ final CodeReviewCommit r = new CodeReviewCommit(ObjectId.zeroId());
+ r.statusCode = s;
+ return r;
+ }
+
+ /**
+ * Unique key of the PatchSet entity from the code review system.
+ * <p>
+ * This value is only available on commits that have a PatchSet represented in
+ * the code review system.
+ */
+ PatchSet.Id patchsetId;
+
+ /** The change containing {@link #patchsetId} . */
+ Change change;
+
+ /**
+ * Ordinal position of this commit within the submit queue.
+ * <p>
+ * Only valid if {@link #patchsetId} is not null.
+ */
+ int originalOrder;
+
+ /**
+ * The result status for this commit.
+ * <p>
+ * Only valid if {@link #patchsetId} is not null.
+ */
+ CommitMergeStatus statusCode;
+
+ /** Commits which are missing ancestors of this commit. */
+ List<CodeReviewCommit> missing;
+
+ CodeReviewCommit(final AnyObjectId id) {
+ super(id);
+ }
+
+ void copyFrom(final CodeReviewCommit src) {
+ patchsetId = src.patchsetId;
+ change = src.change;
+ originalOrder = src.originalOrder;
+ statusCode = src.statusCode;
+ missing = src.missing;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CommitMergeStatus.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CommitMergeStatus.java
new file mode 100644
index 0000000000..788d90b713
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CommitMergeStatus.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+enum CommitMergeStatus {
+ /** */
+ CLEAN_MERGE,
+
+ /** */
+ CLEAN_PICK,
+
+ /** */
+ ALREADY_MERGED,
+
+ /** */
+ PATH_CONFLICT,
+
+ /** */
+ MISSING_DEPENDENCY,
+
+ /** */
+ NO_PATCH_SET,
+
+ /** */
+ REVISION_GONE,
+
+ /** */
+ CRISS_CROSS_MERGE,
+
+ /** */
+ CANNOT_CHERRY_PICK_ROOT,
+
+ /** */
+ NOT_FAST_FORWARD;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultQueueOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultQueueOp.java
new file mode 100644
index 0000000000..82d049302a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/DefaultQueueOp.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import java.util.concurrent.TimeUnit;
+
+public abstract class DefaultQueueOp implements Runnable {
+ private final WorkQueue workQueue;
+
+ protected DefaultQueueOp(final WorkQueue wq) {
+ workQueue = wq;
+ }
+
+ public void start(final int delay, final TimeUnit unit) {
+ workQueue.getDefaultQueue().schedule(this, delay, unit);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
new file mode 100644
index 0000000000..8adad05aa2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
@@ -0,0 +1,175 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.SitePath;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.LockFile;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
+
+import java.io.File;
+import java.io.IOException;
+
+/** Class managing Git repositories. */
+@Singleton
+public class GitRepositoryManager {
+ private static final Logger log = LoggerFactory.getLogger(GitRepositoryManager.class);
+ private final File sitePath;
+ private final File basepath;
+
+ @Inject
+ GitRepositoryManager(@SitePath final File path, @GerritServerConfig final Config cfg) {
+ sitePath = path;
+
+ final String basePath = cfg.getString("gerrit", null, "basepath");
+ if (basePath != null) {
+ File root = new File(basePath);
+ if (!root.isAbsolute()) {
+ root = new File(sitePath, basePath);
+ }
+ basepath = root;
+ } else {
+ basepath = null;
+ }
+ }
+
+ /**
+ * Get (or open) a repository by name.
+ *
+ * @param name the repository name, relative to the base directory.
+ * @return the cached Repository instance. Caller must call {@code close()}
+ * when done to decrement the resource handle.
+ * @throws RepositoryNotFoundException the name does not denote an existing
+ * repository, or the name cannot be read as a repository.
+ */
+ public Repository openRepository(String name)
+ throws RepositoryNotFoundException {
+ if (basepath == null) {
+ throw new RepositoryNotFoundException("No gerrit.basepath configured");
+ }
+
+ if (isUnreasonableName(name)) {
+ throw new RepositoryNotFoundException("Invalid name: " + name);
+ }
+
+ try {
+ final FileKey loc = FileKey.lenient(new File(basepath, name));
+ return RepositoryCache.open(loc);
+ } catch (IOException e1) {
+ final RepositoryNotFoundException e2;
+ e2 = new RepositoryNotFoundException("Cannot open repository " + name);
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ /**
+ * Create (and open) a repository by name.
+ *
+ * @param name the repository name, relative to the base directory.
+ * @return the cached Repository instance. Caller must call {@code close()}
+ * when done to decrement the resource handle.
+ * @throws RepositoryNotFoundException the name does not denote an existing
+ * repository, or the name cannot be read as a repository.
+ */
+ public Repository createRepository(String name)
+ throws RepositoryNotFoundException {
+ if (basepath == null) {
+ throw new RepositoryNotFoundException("No gerrit.basepath configured");
+ }
+
+ if (isUnreasonableName(name)) {
+ throw new RepositoryNotFoundException("Invalid name: " + name);
+ }
+
+ try {
+ if (!name.endsWith(".git")) {
+ name = name + ".git";
+ }
+ final FileKey loc = FileKey.exact(new File(basepath, name));
+ return RepositoryCache.open(loc, false);
+ } catch (IOException e1) {
+ final RepositoryNotFoundException e2;
+ e2 = new RepositoryNotFoundException("Cannot open repository " + name);
+ e2.initCause(e1);
+ throw e2;
+ }
+ }
+
+ /**
+ * Set the {@code GIT_DIR/description} file for gitweb.
+ * <p>
+ * NB: This code should really be in JGit, as a member of the Repostiory
+ * object. Until it moves there, its here.
+ *
+ * @param name the repository name, relative to the base directory.
+ * @param description new description text for the repository.
+ */
+ public void setProjectDescription(final String name, final String description) {
+ // Update git's description file, in case gitweb is being used
+ //
+ try {
+ final Repository e;
+ final LockFile f;
+
+ e = openRepository(name);
+ f = new LockFile(new File(e.getDirectory(), "description"));
+ if (f.lock()) {
+ String d = description;
+ if (d != null) {
+ d = d.trim();
+ if (d.length() > 0) {
+ d += "\n";
+ }
+ } else {
+ d = "";
+ }
+ f.write(Constants.encode(d));
+ f.commit();
+ }
+ e.close();
+ } catch (RepositoryNotFoundException e) {
+ log.error("Cannot update description for " + name, e);
+ } catch (IOException e) {
+ log.error("Cannot update description for " + name, e);
+ }
+ }
+
+ private boolean isUnreasonableName(final String name) {
+ if (name.length() == 0) return true; // no empty paths
+
+ if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths
+ if (name.charAt(0) == '/') return true; // no absolute paths
+ if (new File(name).isAbsolute()) return true; // no absolute paths
+
+ if (name.startsWith("../")) return true; // no "l../etc/passwd"
+ if (name.contains("/../")) return true; // no "foo/../etc/passwd"
+ if (name.contains("/./")) return true; // "foo/./foo" is insane to ask
+ if (name.contains("//")) return true; // windows UNC path can be "//..."
+
+ return false; // is a reasonable name
+ }
+
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java
new file mode 100644
index 0000000000..44becb525b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeException.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+/** Indicates the current branch's queue cannot be processed at this time. */
+class MergeException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ MergeException(final String msg) {
+ super(msg, null);
+ }
+
+ MergeException(final String msg, final Throwable why) {
+ super(msg, why);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
new file mode 100644
index 0000000000..5f854f58d3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -0,0 +1,1189 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.MergeFailSender;
+import com.google.gerrit.server.mail.MergedSender;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.workflow.CategoryFunction;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.merge.Merger;
+import org.eclipse.jgit.merge.ThreeWayMerger;
+import org.eclipse.jgit.revwalk.FooterKey;
+import org.eclipse.jgit.revwalk.FooterLine;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Merges changes in submission order into a single branch.
+ * <p>
+ * Branches are reduced to the minimum number of heads needed to merge
+ * everything. This allows commits to be entered into the queue in any order
+ * (such as ancestors before descendants) and only the most recent commit on any
+ * line of development will be merged. All unmerged commits along a line of
+ * development must be in the submission queue in order to merge the tip of that
+ * line.
+ * <p>
+ * Conflicts are handled by discarding the entire line of development and
+ * marking it as conflicting, even if an earlier commit along that same line can
+ * be merged cleanly.
+ */
+public class MergeOp {
+ public interface Factory {
+ MergeOp create(Branch.NameKey branch);
+ }
+
+ private static final Logger log = LoggerFactory.getLogger(MergeOp.class);
+ private static final String R_HEADS_MASTER =
+ Constants.R_HEADS + Constants.MASTER;
+ private static final ApprovalCategory.Id CRVW =
+ new ApprovalCategory.Id("CRVW");
+ private static final ApprovalCategory.Id VRIF =
+ new ApprovalCategory.Id("VRIF");
+ private static final FooterKey REVIEWED_ON = new FooterKey("Reviewed-on");
+ private static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
+
+ /** Amount of time to wait between submit and checking for missing deps. */
+ private static final long DEPENDENCY_DELAY =
+ MILLISECONDS.convert(15, MINUTES);
+
+ private final GitRepositoryManager repoManager;
+ private final SchemaFactory<ReviewDb> schemaFactory;
+ private final ProjectCache projectCache;
+ private final FunctionState.Factory functionState;
+ private final ReplicationQueue replication;
+ private final MergedSender.Factory mergedSenderFactory;
+ private final MergeFailSender.Factory mergeFailSenderFactory;
+ private final Provider<String> urlProvider;
+ private final ApprovalTypes approvalTypes;
+ private final PatchSetInfoFactory patchSetInfoFactory;
+ private final IdentifiedUser.GenericFactory identifiedUserFactory;
+ private final MergeQueue mergeQueue;
+
+ private final PersonIdent myIdent;
+ private final Branch.NameKey destBranch;
+ private Project destProject;
+ private final List<CodeReviewCommit> toMerge;
+ private List<Change> submitted;
+ private final Map<Change.Id, CodeReviewCommit> commits;
+ private ReviewDb schema;
+ private Repository db;
+ private RevWalk rw;
+ private RevFlag CAN_MERGE;
+ private CodeReviewCommit branchTip;
+ private CodeReviewCommit mergeTip;
+ private Set<RevCommit> alreadyAccepted;
+ private RefUpdate branchUpdate;
+
+ @Inject
+ MergeOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> sf,
+ final ProjectCache pc, final FunctionState.Factory fs,
+ final ReplicationQueue rq, final MergedSender.Factory msf,
+ final MergeFailSender.Factory mfsf,
+ @CanonicalWebUrl @Nullable final Provider<String> cwu,
+ final ApprovalTypes approvalTypes, final PatchSetInfoFactory psif,
+ final IdentifiedUser.GenericFactory iuf,
+ @GerritPersonIdent final PersonIdent myIdent,
+ final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch) {
+ repoManager = grm;
+ schemaFactory = sf;
+ functionState = fs;
+ projectCache = pc;
+ replication = rq;
+ mergedSenderFactory = msf;
+ mergeFailSenderFactory = mfsf;
+ urlProvider = cwu;
+ this.approvalTypes = approvalTypes;
+ patchSetInfoFactory = psif;
+ identifiedUserFactory = iuf;
+ this.mergeQueue = mergeQueue;
+
+ this.myIdent = myIdent;
+ destBranch = branch;
+ toMerge = new ArrayList<CodeReviewCommit>();
+ commits = new HashMap<Change.Id, CodeReviewCommit>();
+ }
+
+ public void merge() throws MergeException {
+ final ProjectState pe = projectCache.get(destBranch.getParentKey());
+ if (pe == null) {
+ throw new MergeException("No such project: " + destBranch.getParentKey());
+ }
+ destProject = pe.getProject();
+
+ try {
+ schema = schemaFactory.open();
+ } catch (OrmException e) {
+ throw new MergeException("Cannot open database", e);
+ }
+ try {
+ mergeImpl();
+ } finally {
+ if (db != null) {
+ db.close();
+ }
+ schema.close();
+ schema = null;
+ }
+ }
+
+ private void mergeImpl() throws MergeException {
+ openRepository();
+ openBranch();
+ listPendingSubmits();
+ validateChangeList();
+ mergeTip = branchTip;
+ switch (destProject.getSubmitType()) {
+ case CHERRY_PICK:
+ cherryPickChanges();
+ break;
+
+ case FAST_FORWARD_ONLY:
+ case MERGE_ALWAYS:
+ case MERGE_IF_NECESSARY:
+ default:
+ reduceToMinimalMerge();
+ mergeTopics();
+ markCleanMerges();
+ break;
+ }
+ updateBranch();
+ updateChangeStatus();
+ }
+
+ private void openRepository() throws MergeException {
+ final String name = destBranch.getParentKey().get();
+ try {
+ db = repoManager.openRepository(name);
+ } catch (RepositoryNotFoundException notGit) {
+ final String m = "Repository \"" + name + "\" unknown.";
+ throw new MergeException(m, notGit);
+ }
+
+ rw = new RevWalk(db) {
+ @Override
+ protected RevCommit createCommit(final AnyObjectId id) {
+ return new CodeReviewCommit(id);
+ }
+ };
+ rw.sort(RevSort.TOPO);
+ rw.sort(RevSort.COMMIT_TIME_DESC, true);
+ CAN_MERGE = rw.newFlag("CAN_MERGE");
+ }
+
+ private void openBranch() throws MergeException {
+ alreadyAccepted = new HashSet<RevCommit>();
+
+ try {
+ branchUpdate = db.updateRef(destBranch.get());
+ if (branchUpdate.getOldObjectId() != null) {
+ branchTip =
+ (CodeReviewCommit) rw.parseCommit(branchUpdate.getOldObjectId());
+ alreadyAccepted.add(branchTip);
+ } else {
+ branchTip = null;
+ }
+
+ for (final Ref r : rw.getRepository().getAllRefs().values()) {
+ if (r.getName().startsWith(Constants.R_HEADS)
+ || r.getName().startsWith(Constants.R_TAGS)) {
+ try {
+ alreadyAccepted.add(rw.parseCommit(r.getObjectId()));
+ } catch (IncorrectObjectTypeException iote) {
+ // Not a commit? Skip over it.
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new MergeException("Cannot open branch", e);
+ }
+ }
+
+ private void listPendingSubmits() throws MergeException {
+ try {
+ submitted = schema.changes().submitted(destBranch).toList();
+ } catch (OrmException e) {
+ throw new MergeException("Cannot query the database", e);
+ }
+ }
+
+ private void validateChangeList() throws MergeException {
+ final Set<ObjectId> tips = new HashSet<ObjectId>();
+ for (final Ref r : db.getAllRefs().values()) {
+ tips.add(r.getObjectId());
+ }
+
+ int commitOrder = 0;
+ for (final Change chg : submitted) {
+ final Change.Id changeId = chg.getId();
+ if (chg.currentPatchSetId() == null) {
+ commits.put(changeId, CodeReviewCommit
+ .error(CommitMergeStatus.NO_PATCH_SET));
+ continue;
+ }
+
+ final PatchSet ps;
+ try {
+ ps = schema.patchSets().get(chg.currentPatchSetId());
+ } catch (OrmException e) {
+ throw new MergeException("Cannot query the database", e);
+ }
+ if (ps == null || ps.getRevision() == null
+ || ps.getRevision().get() == null) {
+ commits.put(changeId, CodeReviewCommit
+ .error(CommitMergeStatus.NO_PATCH_SET));
+ continue;
+ }
+
+ final String idstr = ps.getRevision().get();
+ final ObjectId id;
+ try {
+ id = ObjectId.fromString(idstr);
+ } catch (IllegalArgumentException iae) {
+ commits.put(changeId, CodeReviewCommit
+ .error(CommitMergeStatus.NO_PATCH_SET));
+ continue;
+ }
+
+ if (!tips.contains(id)) {
+ // TODO Technically the proper way to do this test is to use a
+ // RevWalk on "$id --not --all" and test for an empty set. But
+ // that is way slower than looking for a ref directly pointing
+ // at the desired tip. We should always have a ref available.
+ //
+ // TODO this is actually an error, the branch is gone but we
+ // want to merge the issue. We can't safely do that if the
+ // tip is not reachable.
+ //
+ commits.put(changeId, CodeReviewCommit
+ .error(CommitMergeStatus.REVISION_GONE));
+ continue;
+ }
+
+ final CodeReviewCommit commit;
+ try {
+ commit = (CodeReviewCommit) rw.parseCommit(id);
+ } catch (IOException e) {
+ log.error("Invalid commit " + id.name() + " on " + chg.getKey(), e);
+ commits.put(changeId, CodeReviewCommit
+ .error(CommitMergeStatus.REVISION_GONE));
+ continue;
+ }
+
+ commit.change = chg;
+ commit.patchsetId = ps.getId();
+ commit.originalOrder = commitOrder++;
+ commits.put(changeId, commit);
+
+ if (branchTip != null) {
+ // If this commit is already merged its a bug in the queuing code
+ // that we got back here. Just mark it complete and move on. Its
+ // merged and that is all that mattered to the requestor.
+ //
+ try {
+ if (rw.isMergedInto(commit, branchTip)) {
+ commit.statusCode = CommitMergeStatus.ALREADY_MERGED;
+ continue;
+ }
+ } catch (IOException err) {
+ throw new MergeException("Cannot perform merge base test", err);
+ }
+ }
+
+ commit.add(CAN_MERGE);
+ toMerge.add(commit);
+ }
+ }
+
+ private void reduceToMinimalMerge() throws MergeException {
+ final Collection<CodeReviewCommit> heads;
+ try {
+ heads = new MergeSorter(rw, alreadyAccepted, CAN_MERGE).sort(toMerge);
+ } catch (IOException e) {
+ throw new MergeException("Branch head sorting failed", e);
+ }
+
+ toMerge.clear();
+ toMerge.addAll(heads);
+ Collections.sort(toMerge, new Comparator<CodeReviewCommit>() {
+ public int compare(final CodeReviewCommit a, final CodeReviewCommit b) {
+ return a.originalOrder - b.originalOrder;
+ }
+ });
+ }
+
+ private void mergeTopics() throws MergeException {
+ // Take the first fast-forward available, if any is available in the set.
+ //
+ if (destProject.getSubmitType() != Project.SubmitType.MERGE_ALWAYS) {
+ for (final Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext();) {
+ try {
+ final CodeReviewCommit n = i.next();
+ if (mergeTip == null || rw.isMergedInto(mergeTip, n)) {
+ mergeTip = n;
+ i.remove();
+ break;
+ }
+ } catch (IOException e) {
+ throw new MergeException("Cannot fast-forward test during merge", e);
+ }
+ }
+ }
+
+ if (destProject.getSubmitType() == Project.SubmitType.FAST_FORWARD_ONLY) {
+ // If this project only permits fast-forwards, abort everything else.
+ //
+ while (!toMerge.isEmpty()) {
+ final CodeReviewCommit n = toMerge.remove(0);
+ n.statusCode = CommitMergeStatus.NOT_FAST_FORWARD;
+ }
+
+ } else {
+ // For every other commit do a pair-wise merge.
+ //
+ while (!toMerge.isEmpty()) {
+ mergeOneCommit(toMerge.remove(0));
+ }
+ }
+ }
+
+ private void mergeOneCommit(final CodeReviewCommit n) throws MergeException {
+ final Merger m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ try {
+ if (m.merge(new AnyObjectId[] {mergeTip, n})) {
+ writeMergeCommit(m, n);
+
+ } else {
+ failed(n, CommitMergeStatus.PATH_CONFLICT);
+ }
+ } catch (IOException e) {
+ if (e.getMessage().startsWith("Multiple merge bases for")) {
+ try {
+ failed(n, CommitMergeStatus.CRISS_CROSS_MERGE);
+ } catch (IOException e2) {
+ throw new MergeException("Cannot merge " + n.name(), e);
+ }
+ } else {
+ throw new MergeException("Cannot merge " + n.name(), e);
+ }
+ }
+ }
+
+ private CodeReviewCommit failed(final CodeReviewCommit n,
+ final CommitMergeStatus failure) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ rw.reset();
+ rw.markStart(n);
+ rw.markUninteresting(mergeTip);
+ CodeReviewCommit failed;
+ while ((failed = (CodeReviewCommit) rw.next()) != null) {
+ failed.statusCode = failure;
+ }
+ return failed;
+ }
+
+ private void writeMergeCommit(final Merger m, final CodeReviewCommit n)
+ throws IOException, MissingObjectException, IncorrectObjectTypeException {
+ final List<CodeReviewCommit> merged = new ArrayList<CodeReviewCommit>();
+ rw.reset();
+ rw.markStart(n);
+ rw.markUninteresting(mergeTip);
+ for (final RevCommit c : rw) {
+ final CodeReviewCommit crc = (CodeReviewCommit) c;
+ if (crc.patchsetId != null) {
+ merged.add(crc);
+ }
+ }
+
+ final StringBuilder msgbuf = new StringBuilder();
+ if (merged.size() == 1) {
+ final CodeReviewCommit c = merged.get(0);
+ msgbuf.append("Merge change ");
+ msgbuf.append(c.change.getKey().abbreviate());
+ } else {
+ final ArrayList<CodeReviewCommit> o;
+ o = new ArrayList<CodeReviewCommit>(merged);
+ Collections.sort(o, new Comparator<CodeReviewCommit>() {
+ public int compare(final CodeReviewCommit a, final CodeReviewCommit b) {
+ final Change.Id aId = a.patchsetId.getParentKey();
+ final Change.Id bId = b.patchsetId.getParentKey();
+ return aId.get() - bId.get();
+ }
+ });
+
+ msgbuf.append("Merge changes ");
+ for (final Iterator<CodeReviewCommit> i = o.iterator(); i.hasNext();) {
+ msgbuf.append(i.next().change.getKey().abbreviate());
+ if (i.hasNext()) {
+ msgbuf.append(',');
+ }
+ }
+ }
+
+ if (!R_HEADS_MASTER.equals(destBranch.get())) {
+ msgbuf.append(" into ");
+ msgbuf.append(destBranch.getShortName());
+ }
+ msgbuf.append("\n\n* changes:\n");
+ for (final CodeReviewCommit c : merged) {
+ msgbuf.append(" ");
+ msgbuf.append(c.getShortMessage());
+ msgbuf.append("\n");
+ }
+
+ final Commit mergeCommit = new Commit(db);
+ mergeCommit.setTreeId(m.getResultTreeId());
+ mergeCommit.setParentIds(new ObjectId[] {mergeTip, n});
+ mergeCommit.setAuthor(myIdent);
+ mergeCommit.setCommitter(mergeCommit.getAuthor());
+ mergeCommit.setMessage(msgbuf.toString());
+
+ final ObjectId id = m.getObjectWriter().writeCommit(mergeCommit);
+ mergeTip = (CodeReviewCommit) rw.parseCommit(id);
+ }
+
+ private void markCleanMerges() throws MergeException {
+ if (mergeTip == null) {
+ // If mergeTip is null here, branchTip was null, indicating a new branch
+ // at the start of the merge process. We also elected to merge nothing,
+ // probably due to missing dependencies. Nothing was cleanly merged.
+ //
+ return;
+ }
+
+ try {
+ rw.reset();
+ rw.sort(RevSort.TOPO);
+ rw.sort(RevSort.REVERSE, true);
+ rw.markStart(mergeTip);
+ for (RevCommit c : alreadyAccepted) {
+ rw.markUninteresting(c);
+ }
+
+ CodeReviewCommit c;
+ while ((c = (CodeReviewCommit) rw.next()) != null) {
+ if (c.patchsetId != null) {
+ c.statusCode = CommitMergeStatus.CLEAN_MERGE;
+ if (branchUpdate.getRefLogIdent() == null) {
+ setRefLogIdent(getSubmitter(c.patchsetId));
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new MergeException("Cannot mark clean merges", e);
+ }
+ }
+
+ private void setRefLogIdent(final PatchSetApproval submitAudit) {
+ if (submitAudit != null) {
+ branchUpdate.setRefLogIdent(identifiedUserFactory.create(
+ submitAudit.getAccountId()).newPersonIdent());
+ }
+ }
+
+ private void cherryPickChanges() throws MergeException {
+ while (!toMerge.isEmpty()) {
+ final CodeReviewCommit n = toMerge.remove(0);
+ final ThreeWayMerger m;
+
+ m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
+ try {
+ if (mergeTip == null) {
+ // The branch is unborn. Take a fast-forward resolution to
+ // create the branch.
+ //
+ mergeTip = n;
+ n.statusCode = CommitMergeStatus.CLEAN_MERGE;
+
+ } else if (n.getParentCount() == 0) {
+ // Refuse to merge a root commit into an existing branch,
+ // we cannot obtain a delta for the cherry-pick to apply.
+ //
+ n.statusCode = CommitMergeStatus.CANNOT_CHERRY_PICK_ROOT;
+
+ } else if (n.getParentCount() == 1) {
+ // If there is only one parent, a cherry-pick can be done by
+ // taking the delta relative to that one parent and redoing
+ // that on the current merge tip.
+ //
+ m.setBase(n.getParent(0));
+ if (m.merge(mergeTip, n)) {
+ writeCherryPickCommit(m, n);
+
+ } else {
+ n.statusCode = CommitMergeStatus.PATH_CONFLICT;
+ }
+
+ } else {
+ // There are multiple parents, so this is a merge commit. We
+ // don't want to cherry-pick the merge as clients can't easily
+ // rebase their history with that merge present and replaced
+ // by an equivalent merge with a different first parent. So
+ // instead behave as though MERGE_IF_NECESSARY was configured.
+ //
+ if (hasDependenciesMet(n)) {
+ if (rw.isMergedInto(mergeTip, n)) {
+ mergeTip = n;
+ } else {
+ mergeOneCommit(n);
+ }
+ markCleanMerges();
+
+ } else {
+ // One or more dependencies were not met. The status was
+ // already marked on the commit so we have nothing further
+ // to perform at this time.
+ //
+ }
+ }
+
+ } catch (IOException e) {
+ throw new MergeException("Cannot merge " + n.name(), e);
+ }
+ }
+ }
+
+ private boolean hasDependenciesMet(final CodeReviewCommit n)
+ throws IOException {
+ // Oddly we can determine this by running the merge sorter and
+ // look for the one commit to come out as a result. This works
+ // as the merge sorter checks the dependency chain as part of
+ // its logic trying to find a minimal merge path.
+ //
+ return new MergeSorter(rw, alreadyAccepted, CAN_MERGE).sort(
+ Collections.singleton(n)).contains(n);
+ }
+
+ private void writeCherryPickCommit(final Merger m, final CodeReviewCommit n)
+ throws IOException {
+ rw.parseBody(n);
+
+ final List<FooterLine> footers = n.getFooterLines();
+ final StringBuilder msgbuf = new StringBuilder();
+ msgbuf.append(n.getFullMessage());
+
+ if (msgbuf.length() == 0) {
+ // WTF, an empty commit message?
+ msgbuf.append("<no commit message provided>");
+ }
+ if (msgbuf.charAt(msgbuf.length() - 1) != '\n') {
+ // Missing a trailing LF? Correct it (perhaps the editor was broken).
+ msgbuf.append('\n');
+ }
+ if (footers.isEmpty()) {
+ // Doesn't end in a "Signed-off-by: ..." style line? Add another line
+ // break to start a new paragraph for the reviewed-by tag lines.
+ //
+ msgbuf.append('\n');
+ }
+
+ if (!contains(footers, CHANGE_ID, n.change.getKey().get())) {
+ msgbuf.append(CHANGE_ID.getName());
+ msgbuf.append(": ");
+ msgbuf.append(n.change.getKey().get());
+ msgbuf.append('\n');
+ }
+
+ final String siteUrl = urlProvider.get();
+ if (siteUrl != null) {
+ final String url = siteUrl + n.patchsetId.getParentKey().get();
+ if (!contains(footers, REVIEWED_ON, url)) {
+ msgbuf.append(REVIEWED_ON.getName());
+ msgbuf.append(": ");
+ msgbuf.append(url);
+ msgbuf.append('\n');
+ }
+ }
+
+ PatchSetApproval submitAudit = null;
+ try {
+ final List<PatchSetApproval> approvalList =
+ schema.patchSetApprovals().byPatchSet(n.patchsetId).toList();
+ Collections.sort(approvalList, new Comparator<PatchSetApproval>() {
+ public int compare(final PatchSetApproval a, final PatchSetApproval b) {
+ return a.getGranted().compareTo(b.getGranted());
+ }
+ });
+
+ for (final PatchSetApproval a : approvalList) {
+ if (a.getValue() <= 0) {
+ // Negative votes aren't counted.
+ continue;
+ }
+
+ if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+ // Submit is treated specially, below (becomes committer)
+ //
+ if (submitAudit == null
+ || a.getGranted().compareTo(submitAudit.getGranted()) > 0) {
+ submitAudit = a;
+ }
+ continue;
+ }
+
+ final Account acc =
+ identifiedUserFactory.create(a.getAccountId()).getAccount();
+ final StringBuilder identbuf = new StringBuilder();
+ if (acc.getFullName() != null && acc.getFullName().length() > 0) {
+ if (identbuf.length() > 0) {
+ identbuf.append(' ');
+ }
+ identbuf.append(acc.getFullName());
+ }
+ if (acc.getPreferredEmail() != null
+ && acc.getPreferredEmail().length() > 0) {
+ if (isSignedOffBy(footers, acc.getPreferredEmail())) {
+ continue;
+ }
+ if (identbuf.length() > 0) {
+ identbuf.append(' ');
+ }
+ identbuf.append('<');
+ identbuf.append(acc.getPreferredEmail());
+ identbuf.append('>');
+ }
+ if (identbuf.length() == 0) {
+ // Nothing reasonable to describe them by? Ignore them.
+ continue;
+ }
+
+ final String tag;
+ if (CRVW.equals(a.getCategoryId())) {
+ tag = "Reviewed-by";
+ } else if (VRIF.equals(a.getCategoryId())) {
+ tag = "Tested-by";
+ } else {
+ final ApprovalType at =
+ approvalTypes.getApprovalType(a.getCategoryId());
+ if (at == null) {
+ // A deprecated/deleted approval type, ignore it.
+ continue;
+ }
+ tag = at.getCategory().getName().replace(' ', '-');
+ }
+
+ if (!contains(footers, new FooterKey(tag), identbuf.toString())) {
+ msgbuf.append(tag);
+ msgbuf.append(": ");
+ msgbuf.append(identbuf);
+ msgbuf.append('\n');
+ }
+ }
+ } catch (OrmException e) {
+ log.error("Can't read approval records for " + n.patchsetId, e);
+ }
+
+ final PersonIdent submitIdent = toPersonIdent(submitAudit);
+ final Commit mergeCommit = new Commit(db);
+ mergeCommit.setTreeId(m.getResultTreeId());
+ mergeCommit.setParentIds(new ObjectId[] {mergeTip});
+ mergeCommit.setAuthor(n.getAuthorIdent());
+ mergeCommit.setCommitter(submitIdent != null ? submitIdent : myIdent);
+ mergeCommit.setMessage(msgbuf.toString());
+
+ final ObjectId id = m.getObjectWriter().writeCommit(mergeCommit);
+ final CodeReviewCommit newCommit = (CodeReviewCommit) rw.parseCommit(id);
+ newCommit.copyFrom(n);
+ newCommit.statusCode = CommitMergeStatus.CLEAN_PICK;
+ commits.put(newCommit.patchsetId.getParentKey(), newCommit);
+
+ mergeTip = newCommit;
+ setRefLogIdent(submitAudit);
+ }
+
+ private boolean contains(List<FooterLine> footers, FooterKey key, String val) {
+ for (final FooterLine line : footers) {
+ if (line.matches(key) && val.equals(line.getValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSignedOffBy(List<FooterLine> footers, String email) {
+ for (final FooterLine line : footers) {
+ if (line.matches(FooterKey.SIGNED_OFF_BY)
+ && email.equals(line.getEmailAddress())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private PersonIdent toPersonIdent(final PatchSetApproval audit) {
+ if (audit == null) {
+ return null;
+ }
+ return identifiedUserFactory.create(audit.getAccountId()).newPersonIdent(
+ audit.getGranted(), myIdent.getTimeZone());
+ }
+
+ private void updateBranch() throws MergeException {
+ if (branchTip == null || branchTip != mergeTip) {
+ branchUpdate.setForceUpdate(false);
+ branchUpdate.setNewObjectId(mergeTip);
+ branchUpdate.setRefLogMessage("merged", true);
+ try {
+ switch (branchUpdate.update(rw)) {
+ case NEW:
+ case FAST_FORWARD:
+ replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate
+ .getName());
+ break;
+
+ default:
+ throw new IOException(branchUpdate.getResult().name());
+ }
+ } catch (IOException e) {
+ throw new MergeException("Cannot update " + branchUpdate.getName(), e);
+ }
+ }
+ }
+
+ private void updateChangeStatus() {
+ for (final Change c : submitted) {
+ final CodeReviewCommit commit = commits.get(c.getId());
+ final CommitMergeStatus s = commit != null ? commit.statusCode : null;
+ if (s == null) {
+ // Shouldn't ever happen, but leave the change alone. We'll pick
+ // it up on the next pass.
+ //
+ continue;
+ }
+
+ switch (s) {
+ case CLEAN_MERGE: {
+ final String txt =
+ "Change has been successfully merged into the git repository.";
+ setMerged(c, message(c, txt));
+ break;
+ }
+
+ case CLEAN_PICK: {
+ final String txt =
+ "Change has been successfully cherry-picked as " + commit.name()
+ + ".";
+ setMerged(c, message(c, txt));
+ break;
+ }
+
+ case ALREADY_MERGED:
+ setMerged(c, null);
+ break;
+
+ case PATH_CONFLICT: {
+ final String txt =
+ "Your change could not be merged due to a path conflict.\n"
+ + "\n"
+ + "Please merge (or rebase) the change locally and upload the resolution for review.";
+ setNew(c, message(c, txt));
+ break;
+ }
+
+ case CRISS_CROSS_MERGE: {
+ final String txt =
+ "Your change requires a recursive merge to resolve.\n"
+ + "\n"
+ + "Please merge (or rebase) the change locally and upload the resolution for review.";
+ setNew(c, message(c, txt));
+ break;
+ }
+
+ case CANNOT_CHERRY_PICK_ROOT: {
+ final String txt =
+ "Cannot cherry-pick an initial commit onto an existing branch.\n"
+ + "\n"
+ + "Please merge the change locally and upload the merge commit for review.";
+ setNew(c, message(c, txt));
+ break;
+ }
+
+ case NOT_FAST_FORWARD: {
+ final String txt =
+ "Project policy requires all submissions to be a fast-forward.\n"
+ + "\n"
+ + "Please rebase the change locally and upload again for review.";
+ setNew(c, message(c, txt));
+ break;
+ }
+
+ case MISSING_DEPENDENCY: {
+ dependencyError(commit);
+ break;
+ }
+
+ default:
+ setNew(c, message(c, "Unspecified merge failure: " + s.name()));
+ break;
+ }
+ }
+ }
+
+ private void dependencyError(final CodeReviewCommit commit) {
+ final Change c = commit.change;
+ if (commit.missing == null) {
+ commit.missing = new ArrayList<CodeReviewCommit>();
+ }
+
+ boolean submitStillPossible = commit.missing.size() > 0;
+ for (CodeReviewCommit missingCommit : commit.missing) {
+ loadChangeInfo(missingCommit);
+
+ if (missingCommit.patchsetId == null) {
+ // The commit doesn't have a patch set, so it cannot be
+ // submitted to the branch.
+ //
+ submitStillPossible = false;
+ break;
+ }
+
+ if (!missingCommit.change.currentPatchSetId().equals(
+ missingCommit.patchsetId)) {
+ // If the missing commit is not the current patch set,
+ // the change must be rebased to use the proper parent.
+ //
+ submitStillPossible = false;
+ break;
+ }
+ }
+
+ final long now = System.currentTimeMillis();
+ final long waitUntil = c.getLastUpdatedOn().getTime() + DEPENDENCY_DELAY;
+ if (submitStillPossible && now < waitUntil) {
+ // If we waited a short while we might still be able to get
+ // this change submitted. Reschedule an attempt in a bit.
+ //
+ mergeQueue.recheckAfter(destBranch, waitUntil - now, MILLISECONDS);
+
+ } else if (submitStillPossible) {
+ // It would be possible to submit the change if the missing
+ // dependencies are also submitted. Perhaps the user just
+ // forgot to submit those.
+ //
+ String txt =
+ "Change could not be merged because of a missing dependency.";
+ if (!isAlreadySent(c, txt)) {
+ StringBuilder m = new StringBuilder();
+ m.append(txt);
+ m.append("\n");
+
+ m.append("\n");
+
+ m.append("The following changes must also be submitted:\n");
+ m.append("\n");
+ for (CodeReviewCommit missingCommit : commit.missing) {
+ m.append("* ");
+ m.append(missingCommit.change.getKey().get());
+ m.append("\n");
+ }
+ txt = m.toString();
+ }
+
+ sendMergeFail(c, message(c, txt), false);
+
+ } else {
+ // It is impossible to submit this change as-is. The author
+ // needs to rebase it in order to work around the missing
+ // dependencies.
+ //
+ StringBuilder m = new StringBuilder();
+ m.append("Change cannot be merged due"
+ + " to unsatisfiable dependencies.\n");
+ m.append("\n");
+ m.append("The following dependency errors were found:\n");
+ m.append("\n");
+ for (CodeReviewCommit missingCommit : commit.missing) {
+ if (missingCommit.patchsetId != null) {
+ m.append("* Depends on patch set ");
+ m.append(missingCommit.patchsetId.get());
+ m.append(" of ");
+ m.append(missingCommit.change.getKey().abbreviate());
+ m.append(", however the current patch set is ");
+ m.append(missingCommit.change.currentPatchSetId().get());
+ m.append(".\n");
+
+ } else {
+ m.append("* Depends on commit ");
+ m.append(missingCommit.name());
+ m.append(" which has no change associated with it.\n");
+ }
+ }
+ m.append("\n");
+ m.append("Please rebase the change and upload a replacement commit.");
+
+ setNew(c, message(c, m.toString()));
+ }
+ }
+
+ private void loadChangeInfo(final CodeReviewCommit commit) {
+ if (commit.patchsetId == null) {
+ try {
+ List<PatchSet> matches =
+ schema.patchSets().byRevision(new RevId(commit.name())).toList();
+ if (matches.size() == 1) {
+ final PatchSet ps = matches.get(0);
+ commit.patchsetId = ps.getId();
+ commit.change = schema.changes().get(ps.getId().getParentKey());
+ }
+ } catch (OrmException e) {
+ }
+ }
+ }
+
+ private boolean isAlreadySent(final Change c, final String prefix) {
+ try {
+ final List<ChangeMessage> msgList =
+ schema.changeMessages().byChange(c.getId()).toList();
+ if (msgList.size() > 0) {
+ final ChangeMessage last = msgList.get(msgList.size() - 1);
+ if (last.getAuthor() == null && last.getMessage().startsWith(prefix)) {
+ // The last message was written by us, and it said this
+ // same message already. Its unlikely anything has changed
+ // that would cause us to need to repeat ourselves.
+ //
+ return true;
+ }
+ }
+
+ // The last message was not sent by us, or doesn't match the text
+ // we are about to send.
+ //
+ return false;
+ } catch (OrmException e) {
+ return true;
+ }
+ }
+
+ private ChangeMessage message(final Change c, final String body) {
+ final String uuid;
+ try {
+ uuid = ChangeUtil.messageUUID(schema);
+ } catch (OrmException e) {
+ return null;
+ }
+ final ChangeMessage m =
+ new ChangeMessage(new ChangeMessage.Key(c.getId(), uuid), null);
+ m.setMessage(body);
+ return m;
+ }
+
+ private PatchSetApproval getSubmitter(PatchSet.Id c) {
+ if (c == null) {
+ return null;
+ }
+ PatchSetApproval submitter = null;
+ try {
+ final List<PatchSetApproval> approvals =
+ schema.patchSetApprovals().byPatchSet(c).toList();
+ for (PatchSetApproval a : approvals) {
+ if (a.getValue() > 0
+ && ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+ if (submitter == null
+ || a.getGranted().compareTo(submitter.getGranted()) > 0) {
+ submitter = a;
+ }
+ }
+ }
+ } catch (OrmException e) {
+ }
+ return submitter;
+ }
+
+ private void setMerged(Change c, ChangeMessage msg) {
+ final PatchSet.Id merged = c.currentPatchSetId();
+ PatchSetApproval submitter = null;
+ for (int attempts = 0; attempts < 10; attempts++) {
+ c.setStatus(Change.Status.MERGED);
+ ChangeUtil.updated(c);
+ try {
+ final Transaction txn = schema.beginTransaction();
+
+ // Flatten out all existing approvals based upon the current
+ // permissions. Once the change is closed the approvals are
+ // not updated at presentation view time, so we need to make.
+ // sure they are accurate now. This way if permissions get
+ // modified in the future, historical records stay accurate.
+ //
+ final List<PatchSetApproval> approvals =
+ schema.patchSetApprovals().byChange(c.getId()).toList();
+ final FunctionState fs = functionState.create(c, merged, approvals);
+ for (ApprovalType at : approvalTypes.getApprovalTypes()) {
+ CategoryFunction.forCategory(at.getCategory()).run(at, fs);
+ }
+ for (PatchSetApproval a : approvals) {
+ if (a.getValue() > 0
+ && ApprovalCategory.SUBMIT.equals(a.getCategoryId())
+ && a.getPatchSetId().equals(merged)) {
+ if (submitter == null
+ || a.getGranted().compareTo(submitter.getGranted()) > 0) {
+ submitter = a;
+ }
+ }
+ a.cache(c);
+ }
+ schema.patchSetApprovals().update(approvals, txn);
+
+ if (msg != null) {
+ if (submitter != null && msg.getAuthor() == null) {
+ msg.setAuthor(submitter.getAccountId());
+ }
+ schema.changeMessages().insert(Collections.singleton(msg), txn);
+ }
+ schema.changes().update(Collections.singleton(c), txn);
+ txn.commit();
+ break;
+ } catch (OrmException e) {
+ final Change.Id id = c.getId();
+ try {
+ c = schema.changes().get(id);
+ if (!merged.equals(c.currentPatchSetId())) {
+ // Uncool; the patch set changed after we merged it.
+ // Go back to the patch set that was actually merged.
+ //
+ try {
+ c.setCurrentPatchSet(patchSetInfoFactory.get(merged));
+ } catch (PatchSetInfoNotAvailableException e1) {
+ log.error("Cannot read merged patch set " + merged, e1);
+ }
+ }
+ } catch (OrmException e2) {
+ log.error("Cannot set change " + id + " to merged " + merged, e2);
+ }
+ }
+ }
+
+ try {
+ final MergedSender cm = mergedSenderFactory.create(c);
+ if (submitter != null) {
+ cm.setFrom(submitter.getAccountId());
+ }
+ cm.setReviewDb(schema);
+ cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
+ cm.send();
+ } catch (OrmException e) {
+ log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ } catch (EmailException e) {
+ log.error("Cannot send email for submitted patch set " + c.getId(), e);
+ }
+ }
+
+ private void setNew(Change c, ChangeMessage msg) {
+ sendMergeFail(c, msg, true);
+ }
+
+ private void sendMergeFail(Change c, ChangeMessage msg, boolean makeNew) {
+ for (int attempts = 0; attempts < 10; attempts++) {
+ if (makeNew) {
+ c.setStatus(Change.Status.NEW);
+ }
+ ChangeUtil.updated(c);
+ try {
+ final Transaction txn = schema.beginTransaction();
+ schema.changes().update(Collections.singleton(c), txn);
+ if (msg != null) {
+ schema.changeMessages().insert(Collections.singleton(msg), txn);
+ }
+ txn.commit();
+ break;
+ } catch (OrmException e) {
+ try {
+ c = schema.changes().get(c.getId());
+ if (c.getStatus().isClosed()) {
+ // Someone else marked it close while we noticed a failure.
+ // That's fine, leave it closed.
+ //
+ break;
+ }
+ } catch (OrmException e2) {
+ }
+ }
+ }
+
+ try {
+ final PatchSetApproval submitter = getSubmitter(c.currentPatchSetId());
+ final MergeFailSender cm = mergeFailSenderFactory.create(c);
+ if (submitter != null) {
+ cm.setFrom(submitter.getAccountId());
+ }
+ cm.setReviewDb(schema);
+ cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
+ cm.setChangeMessage(msg);
+ cm.send();
+ } catch (OrmException e) {
+ log.error("Cannot send email notifications about merge failure", e);
+ } catch (EmailException e) {
+ log.error("Cannot send email notifications about merge failure", e);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeQueue.java
new file mode 100644
index 0000000000..abb0ab81c6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeQueue.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.Branch;
+
+import java.util.concurrent.TimeUnit;
+
+public interface MergeQueue {
+ void merge(Branch.NameKey branch);
+
+ void schedule(Branch.NameKey branch);
+
+ void recheckAfter(Branch.NameKey branch, long delay, TimeUnit delayUnit);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSorter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSorter.java
new file mode 100644
index 0000000000..fe87480b15
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSorter.java
@@ -0,0 +1,92 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevCommitList;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+class MergeSorter {
+ private final RevWalk rw;
+ private final RevFlag CAN_MERGE;
+ private final Set<RevCommit> accepted;
+
+ MergeSorter(final RevWalk walk, final Set<RevCommit> alreadyAccepted,
+ final RevFlag flagCAN_MERGE) {
+ rw = walk;
+ CAN_MERGE = flagCAN_MERGE;
+ accepted = alreadyAccepted;
+ }
+
+ Collection<CodeReviewCommit> sort(final Collection<CodeReviewCommit> incoming)
+ throws IOException {
+ final Set<CodeReviewCommit> heads = new HashSet<CodeReviewCommit>();
+ final Set<CodeReviewCommit> sort = new HashSet<CodeReviewCommit>(incoming);
+ while (!sort.isEmpty()) {
+ final CodeReviewCommit n = removeOne(sort);
+
+ rw.resetRetain(CAN_MERGE);
+ rw.markStart(n);
+ for (RevCommit c : accepted) {
+ rw.markUninteresting(c);
+ }
+
+ RevCommit c;
+ final RevCommitList<RevCommit> contents = new RevCommitList<RevCommit>();
+ while ((c = rw.next()) != null) {
+ if (!c.has(CAN_MERGE)) {
+ // We cannot merge n as it would bring something we
+ // aren't permitted to merge at this time. Drop n.
+ //
+ if (n.missing == null) {
+ n.statusCode = CommitMergeStatus.MISSING_DEPENDENCY;
+ n.missing = new ArrayList<CodeReviewCommit>();
+ }
+ n.missing.add((CodeReviewCommit) c);
+ } else {
+ contents.add(c);
+ }
+ }
+
+ if (n.statusCode == CommitMergeStatus.MISSING_DEPENDENCY) {
+ continue;
+ }
+
+ // Anything reachable through us is better merged by just
+ // merging us directly. So prune our ancestors out and let
+ // us merge instead.
+ //
+ sort.removeAll(contents);
+ heads.removeAll(contents);
+ heads.add(n);
+ }
+ return heads;
+ }
+
+ private static <T> T removeOne(final Collection<T> c) {
+ final Iterator<T> i = c.iterator();
+ final T r = i.next();
+ i.remove();
+ return r;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PatchSetImporter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PatchSetImporter.java
new file mode 100644
index 0000000000..80ad408cc8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PatchSetImporter.java
@@ -0,0 +1,127 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetAncestor;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Imports a {@link PatchSet} from a {@link Commit}. */
+public class PatchSetImporter {
+ public interface Factory {
+ PatchSetImporter create(ReviewDb dstDb, RevCommit srcCommit,
+ PatchSet dstPatchSet, boolean isNewPatchSet);
+ }
+
+ private final PatchSetInfoFactory patchSetInfoFactory;
+ private final ReviewDb db;
+ private final RevCommit src;
+ private final PatchSet dst;
+ private final boolean isNew;
+ private Transaction txn;
+
+ private PatchSetInfo info;
+
+ private final Map<Integer, PatchSetAncestor> ancestorExisting =
+ new HashMap<Integer, PatchSetAncestor>();
+ private final List<PatchSetAncestor> ancestorInsert =
+ new ArrayList<PatchSetAncestor>();
+ private final List<PatchSetAncestor> ancestorUpdate =
+ new ArrayList<PatchSetAncestor>();
+
+ @Inject
+ PatchSetImporter(final PatchSetInfoFactory psif,
+ @Assisted final ReviewDb dstDb, @Assisted final RevCommit srcCommit,
+ @Assisted final PatchSet dstPatchSet,
+ @Assisted final boolean isNewPatchSet) {
+ patchSetInfoFactory = psif;
+ db = dstDb;
+ src = srcCommit;
+ dst = dstPatchSet;
+ isNew = isNewPatchSet;
+ }
+
+ public void setTransaction(final Transaction t) {
+ txn = t;
+ }
+
+ public PatchSetInfo getPatchSetInfo() {
+ return info;
+ }
+
+ public void run() throws OrmException {
+ dst.setRevision(toRevId(src));
+
+ if (!isNew) {
+ for (final PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(
+ dst.getId())) {
+ ancestorExisting.put(a.getPosition(), a);
+ }
+ }
+
+ info = patchSetInfoFactory.get(src, dst.getId());
+ importAncestors();
+
+ final boolean auto = txn == null;
+ if (auto) {
+ txn = db.beginTransaction();
+ }
+ if (isNew) {
+ db.patchSets().insert(Collections.singleton(dst), txn);
+ }
+ db.patchSetAncestors().insert(ancestorInsert, txn);
+ if (!isNew) {
+ db.patchSetAncestors().update(ancestorUpdate, txn);
+ db.patchSetAncestors().delete(ancestorExisting.values(), txn);
+ }
+ if (auto) {
+ txn.commit();
+ txn = null;
+ }
+ }
+
+ private void importAncestors() {
+ for (int p = 0; p < src.getParentCount(); p++) {
+ PatchSetAncestor a = ancestorExisting.remove(p + 1);
+ if (a == null) {
+ a = new PatchSetAncestor(new PatchSetAncestor.Id(dst.getId(), p + 1));
+ ancestorInsert.add(a);
+ } else {
+ ancestorUpdate.add(a);
+ }
+ a.setAncestorRevision(toRevId(src.getParent(p)));
+ }
+ }
+
+ private static RevId toRevId(final RevCommit src) {
+ return new RevId(src.getId().name());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
new file mode 100644
index 0000000000..5e176d6a5a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
@@ -0,0 +1,91 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+public class PushAllProjectsOp extends DefaultQueueOp {
+ public interface Factory {
+ PushAllProjectsOp create(String urlMatch);
+ }
+
+ private static final Logger log =
+ LoggerFactory.getLogger(PushAllProjectsOp.class);
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final ReplicationQueue replication;
+ private final Project.NameKey wildProject;
+ private final String urlMatch;
+
+ @Inject
+ public PushAllProjectsOp(final WorkQueue wq,
+ final SchemaFactory<ReviewDb> sf, final ReplicationQueue rq,
+ @WildProjectName final Project.NameKey wp,
+ @Assisted @Nullable final String urlMatch) {
+ super(wq);
+ this.schema = sf;
+ this.replication = rq;
+ this.wildProject = wp;
+ this.urlMatch = urlMatch;
+ }
+
+ @Override
+ public void start(final int delay, final TimeUnit unit) {
+ if (replication.isEnabled()) {
+ super.start(delay, unit);
+ }
+ }
+
+ public void run() {
+ final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ for (final Project project : db.projects().all()) {
+ if (!project.getNameKey().equals(wildProject)) {
+ replication.scheduleFullSync(project.getNameKey(), urlMatch);
+ }
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ log.error("Cannot enumerate known projects", e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ String s = "Replicate All Projects";
+ if (urlMatch != null) {
+ s = s + " to " + urlMatch;
+ }
+ return s;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushOp.java
new file mode 100644
index 0000000000..cdc05cd5fd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushOp.java
@@ -0,0 +1,291 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import com.jcraft.jsch.JSchException;
+
+import org.slf4j.Logger;
+import org.eclipse.jgit.errors.NoRemoteRepositoryException;
+import org.eclipse.jgit.errors.NotSupportedException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.FetchConnection;
+import org.eclipse.jgit.transport.PushResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.RemoteRefUpdate;
+import org.eclipse.jgit.transport.Transport;
+import org.eclipse.jgit.transport.URIish;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A push to remote operation started by {@link ReplicationQueue}.
+ * <p>
+ * Instance members are protected by the lock within PushQueue. Callers must
+ * take that lock to ensure they are working with a current view of the object.
+ */
+class PushOp implements Runnable {
+ interface Factory {
+ PushOp create(String d, URIish u);
+ }
+
+ private static final Logger log = PushReplication.log;
+ static final String MIRROR_ALL = "..all..";
+
+ private final GitRepositoryManager repoManager;
+ private final PushReplication.ReplicationConfig pool;
+ private final RemoteConfig config;
+
+ private final Set<String> delta = new HashSet<String>();
+ private final String projectName;
+ private final URIish uri;
+ private boolean mirror;
+
+ private Repository db;
+
+ @Inject
+ PushOp(final GitRepositoryManager grm, final PushReplication.ReplicationConfig p,
+ final RemoteConfig c, @Assisted final String d, @Assisted final URIish u) {
+ repoManager = grm;
+ pool = p;
+ config = c;
+ projectName = d;
+ uri = u;
+ }
+
+ URIish getURI() {
+ return uri;
+ }
+
+ void addRef(final String ref) {
+ if (MIRROR_ALL.equals(ref)) {
+ delta.clear();
+ mirror = true;
+ } else if (!mirror) {
+ delta.add(ref);
+ }
+ }
+
+ public void run() {
+ try {
+ // Lock the queue, and remove ourselves, so we can't be modified once
+ // we start replication (instead a new instance, with the same URI, is
+ // created and scheduled for a future point in time.)
+ //
+ pool.notifyStarting(this);
+ db = repoManager.openRepository(projectName);
+ runImpl();
+ } catch (RepositoryNotFoundException e) {
+ log.error("Cannot replicate " + projectName + "; " + e.getMessage());
+
+ } catch (NoRemoteRepositoryException e) {
+ log.error("Cannot replicate to " + uri + "; repository not found");
+
+ } catch (NotSupportedException e) {
+ log.error("Cannot replicate to " + uri, e);
+
+ } catch (TransportException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof JSchException
+ && cause.getMessage().startsWith("UnknownHostKey:")) {
+ log.error("Cannot replicate to " + uri + ": " + cause.getMessage());
+ } else {
+ log.error("Cannot replicate to " + uri, e);
+ }
+
+ } catch (IOException e) {
+ log.error("Cannot replicate to " + uri, e);
+
+ } catch (RuntimeException e) {
+ log.error("Unexpected error during replication to " + uri, e);
+
+ } catch (Error e) {
+ log.error("Unexpected error during replication to " + uri, e);
+
+ } finally {
+ if (db != null) {
+ db.close();
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return (mirror ? "mirror " : "push ") + uri;
+ }
+
+ private void runImpl() throws IOException {
+ final Transport tn = Transport.open(db, uri);
+ final PushResult res;
+ try {
+ res = pushVia(tn);
+ } finally {
+ try {
+ tn.close();
+ } catch (Throwable e2) {
+ log.warn("Unexpected error while closing " + uri, e2);
+ }
+ }
+
+ for (final RemoteRefUpdate u : res.getRemoteUpdates()) {
+ switch (u.getStatus()) {
+ case OK:
+ case UP_TO_DATE:
+ case NON_EXISTING:
+ break;
+
+ case NOT_ATTEMPTED:
+ case AWAITING_REPORT:
+ case REJECTED_NODELETE:
+ case REJECTED_NONFASTFORWARD:
+ case REJECTED_REMOTE_CHANGED:
+ log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
+ + ": status " + u.getStatus().name());
+ break;
+
+ case REJECTED_OTHER_REASON:
+ log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
+ + ", reason: " + u.getMessage());
+ break;
+ }
+ }
+ }
+
+ private PushResult pushVia(final Transport tn) throws IOException,
+ NotSupportedException, TransportException {
+ tn.applyConfig(config);
+
+ final List<RemoteRefUpdate> todo = generateUpdates(tn);
+ if (todo.isEmpty()) {
+ // If we have no commands selected, we have nothing to do.
+ // Calling JGit at this point would just redo the work we
+ // already did, and come up with the same answer. Instead
+ // send back an empty result.
+ //
+ return new PushResult();
+ }
+
+ return tn.push(NullProgressMonitor.INSTANCE, todo);
+ }
+
+ private List<RemoteRefUpdate> generateUpdates(final Transport tn)
+ throws IOException {
+ final List<RemoteRefUpdate> cmds = new ArrayList<RemoteRefUpdate>();
+ final Map<String, Ref> local = db.getAllRefs();
+
+ if (mirror) {
+ final Map<String, Ref> remote = listRemote(tn);
+
+ for (final Ref src : local.values()) {
+ final RefSpec spec = matchSrc(src.getOrigName());
+ if (spec != null) {
+ final Ref dst = remote.get(spec.getDestination());
+ if (dst == null || !src.getObjectId().equals(dst.getObjectId())) {
+ // Doesn't exist yet, or isn't the same value, request to push.
+ //
+ send(cmds, spec);
+ }
+ }
+ }
+
+ for (final Ref ref : remote.values()) {
+ if (!isHEAD(ref)) {
+ final RefSpec spec = matchDst(ref.getName());
+ if (spec != null && !local.containsKey(spec.getSource())) {
+ // No longer on local side, request removal.
+ //
+ delete(cmds, spec);
+ }
+ }
+ }
+
+ } else {
+ for (final String src : delta) {
+ final RefSpec spec = matchSrc(src);
+ if (spec != null) {
+ // If the ref still exists locally, send it, otherwise delete it.
+ //
+ if (local.containsKey(src)) {
+ send(cmds, spec);
+ } else {
+ delete(cmds, spec);
+ }
+ }
+ }
+ }
+
+ return cmds;
+ }
+
+ private Map<String, Ref> listRemote(final Transport tn)
+ throws NotSupportedException, TransportException {
+ final FetchConnection fc = tn.openFetch();
+ try {
+ return fc.getRefsMap();
+ } finally {
+ fc.close();
+ }
+ }
+
+ private RefSpec matchSrc(final String ref) {
+ for (final RefSpec s : config.getPushRefSpecs()) {
+ if (s.matchSource(ref)) {
+ return s.expandFromSource(ref);
+ }
+ }
+ return null;
+ }
+
+ private RefSpec matchDst(final String ref) {
+ for (final RefSpec s : config.getPushRefSpecs()) {
+ if (s.matchDestination(ref)) {
+ return s.expandFromDestination(ref);
+ }
+ }
+ return null;
+ }
+
+ private void send(final List<RemoteRefUpdate> cmds, final RefSpec spec)
+ throws IOException {
+ final String src = spec.getSource();
+ final String dst = spec.getDestination();
+ final boolean force = spec.isForceUpdate();
+ cmds.add(new RemoteRefUpdate(db, src, dst, force, null, null));
+ }
+
+ private void delete(final List<RemoteRefUpdate> cmds, final RefSpec spec)
+ throws IOException {
+ final String dst = spec.getDestination();
+ final boolean force = spec.isForceUpdate();
+ cmds.add(new RemoteRefUpdate(db, null, dst, force, null, null));
+ }
+
+ private static boolean isHEAD(final Ref ref) {
+ return Constants.HEAD.equals(ref.getOrigName());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
new file mode 100644
index 0000000000..84d5b002d4
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
@@ -0,0 +1,433 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.ReplicationUser;
+import com.google.gerrit.server.config.SitePath;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+import com.google.inject.assistedinject.FactoryProvider;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.FileBasedConfig;
+import org.eclipse.jgit.transport.OpenSshConfig;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.SshConfigSessionFactory;
+import org.eclipse.jgit.transport.SshSessionFactory;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.util.QuotedString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/** Manages automatic replication to remote repositories. */
+@Singleton
+public class PushReplication implements ReplicationQueue {
+ static final Logger log = LoggerFactory.getLogger(PushReplication.class);
+
+ static {
+ // Install our own factory which always runs in batch mode, as we
+ // have no UI available for interactive prompting.
+ //
+ SshSessionFactory.setInstance(new SshConfigSessionFactory() {
+ @Override
+ protected void configure(OpenSshConfig.Host hc, Session session) {
+ // Default configuration is batch mode.
+ }
+ });
+ }
+
+ private final Injector injector;
+ private final WorkQueue workQueue;
+ private final List<ReplicationConfig> configs;
+ private final SchemaFactory<ReviewDb> database;
+ private final ReplicationUser.Factory replicationUserFactory;
+
+ @Inject
+ PushReplication(final Injector i, final WorkQueue wq,
+ @SitePath final File sitePath, final ReplicationUser.Factory ruf,
+ final SchemaFactory<ReviewDb> db) throws ConfigInvalidException,
+ IOException {
+ injector = i;
+ workQueue = wq;
+ database = db;
+ replicationUserFactory = ruf;
+ configs = allConfigs(sitePath);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return configs.size() > 0;
+ }
+
+ @Override
+ public void scheduleFullSync(final Project.NameKey project,
+ final String urlMatch) {
+ for (final ReplicationConfig cfg : configs) {
+ for (final URIish uri : cfg.getURIs(project, urlMatch)) {
+ cfg.schedule(project, PushOp.MIRROR_ALL, uri);
+ }
+ }
+ }
+
+ @Override
+ public void scheduleUpdate(final Project.NameKey project, final String ref) {
+ for (final ReplicationConfig cfg : configs) {
+ if (cfg.wouldPushRef(ref)) {
+ for (final URIish uri : cfg.getURIs(project, null)) {
+ cfg.schedule(project, ref, uri);
+ }
+ }
+ }
+ }
+
+ private static String replace(final String pat, final String key,
+ final String val) {
+ final int n = pat.indexOf("${" + key + "}");
+ return pat.substring(0, n) + val + pat.substring(n + 3 + key.length());
+ }
+
+ private List<ReplicationConfig> allConfigs(final File path)
+ throws ConfigInvalidException, IOException {
+ final File cfgFile = new File(path, "replication.config");
+ final FileBasedConfig cfg = new FileBasedConfig(cfgFile);
+
+ if (!cfg.getFile().exists()) {
+ log.warn("No " + cfg.getFile() + "; not replicating");
+ return Collections.emptyList();
+ }
+
+ try {
+ cfg.load();
+ } catch (ConfigInvalidException e) {
+ throw new ConfigInvalidException("Config file " + cfg.getFile()
+ + " is invalid: " + e.getMessage(), e);
+ } catch (IOException e) {
+ throw new IOException("Cannot read " + cfgFile + ": " + e.getMessage(), e);
+ }
+
+ final List<ReplicationConfig> r = new ArrayList<ReplicationConfig>();
+ for (final RemoteConfig c : allRemotes(cfg)) {
+ if (c.getURIs().isEmpty()) {
+ continue;
+ }
+
+ for (final URIish u : c.getURIs()) {
+ if (u.getPath() == null || !u.getPath().contains("${name}")) {
+ throw new ConfigInvalidException("remote." + c.getName() + ".url"
+ + " \"" + u + "\" lacks ${name} placeholder in " + cfg.getFile());
+ }
+ }
+
+ if (c.getPushRefSpecs().isEmpty()) {
+ RefSpec spec = new RefSpec();
+ spec = spec.setSourceDestination("refs/*", "refs/*");
+ spec = spec.setForceUpdate(true);
+ c.addPushRefSpec(spec);
+ }
+
+ r.add(new ReplicationConfig(injector, workQueue, c, cfg, database,
+ replicationUserFactory));
+ }
+ return Collections.unmodifiableList(r);
+ }
+
+ private List<RemoteConfig> allRemotes(final FileBasedConfig cfg)
+ throws ConfigInvalidException {
+ List<String> names = new ArrayList<String>(cfg.getSubsections("remote"));
+ Collections.sort(names);
+
+ final List<RemoteConfig> result = new ArrayList<RemoteConfig>(names.size());
+ for (final String name : names) {
+ try {
+ result.add(new RemoteConfig(cfg, name));
+ } catch (URISyntaxException e) {
+ throw new ConfigInvalidException("remote " + name
+ + " has invalid URL in " + cfg.getFile());
+ }
+ }
+ return result;
+ }
+
+ public void replicateNewProject(Project.NameKey projectName, String head) {
+ if (!isEnabled()) {
+ return;
+ }
+
+ Iterator<ReplicationConfig> configIter = configs.iterator();
+
+ while (configIter.hasNext()) {
+ ReplicationConfig rp = configIter.next();
+ List<URIish> uriList = rp.getURIs(projectName, "*");
+
+ Iterator<URIish> uriIter = uriList.iterator();
+
+ while (uriIter.hasNext()) {
+ replicateProject(uriIter.next(), head);
+ }
+ }
+ }
+
+ private void replicateProject(final URIish replicateURI, final String head) {
+ SshSessionFactory sshFactory = SshSessionFactory.getInstance();
+ Session sshSession;
+ String projectPath = QuotedString.BOURNE.quote(replicateURI.getPath());
+
+ if (!usingSSH(replicateURI)) {
+ log.warn("Cannot create new project on remote site since the connection "
+ + "method is not SSH: " + replicateURI.toString());
+ return;
+ }
+
+ OutputStream errStream = createErrStream();
+ String cmd =
+ "mkdir -p " + projectPath + "&& cd " + projectPath
+ + "&& git init --bare" + "&& git symbolic-ref HEAD "
+ + QuotedString.BOURNE.quote(head);
+
+ try {
+ sshSession =
+ sshFactory.getSession(replicateURI.getUser(), replicateURI.getPass(),
+ replicateURI.getHost(), replicateURI.getPort());
+ sshSession.connect();
+
+ Channel channel = sshSession.openChannel("exec");
+ ((ChannelExec) channel).setCommand(cmd);
+
+ channel.setInputStream(null);
+
+ ((ChannelExec) channel).setErrStream(errStream);
+
+ channel.connect();
+
+ while (!channel.isClosed()) {
+ try {
+ final int delay = 50;
+ Thread.sleep(delay);
+ } catch (InterruptedException e) {
+ }
+ }
+ channel.disconnect();
+ sshSession.disconnect();
+ } catch (JSchException e) {
+ log.error("Communication error when trying to replicate to: "
+ + replicateURI.toString() + "\n" + "Error reported: "
+ + e.getMessage() + "\n" + "Error in communication: "
+ + errStream.toString());
+ }
+ }
+
+ private OutputStream createErrStream() {
+ return new OutputStream() {
+ private StringBuilder all = new StringBuilder();
+ private StringBuilder sb = new StringBuilder();
+
+ public String toString() {
+ String r = all.toString();
+ while (r.endsWith("\n"))
+ r = r.substring(0, r.length() - 1);
+ return r;
+ }
+
+ @Override
+ public void write(final int b) throws IOException {
+ if (b == '\r') {
+ return;
+ }
+
+ sb.append((char) b);
+
+ if (b == '\n') {
+ all.append(sb);
+ sb.setLength(0);
+ }
+ }
+ };
+ }
+
+ private boolean usingSSH(final URIish uri) {
+ final String scheme = uri.getScheme();
+ if (!uri.isRemote()) return false;
+ if (scheme != null && scheme.toLowerCase().contains("ssh")) return true;
+ if (scheme == null && uri.getHost() != null && uri.getPath() != null)
+ return true;
+ return false;
+ }
+
+ static class ReplicationConfig {
+ private final RemoteConfig remote;
+ private final int delay;
+ private final WorkQueue.Executor pool;
+ private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>();
+ private final PushOp.Factory opFactory;
+ private final ProjectControl.Factory projectControlFactory;
+ private final boolean authEnabled;
+
+ ReplicationConfig(final Injector injector, final WorkQueue workQueue,
+ final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db,
+ final ReplicationUser.Factory replicationUserFactory) {
+
+ remote = rc;
+ delay = Math.max(0, getInt(rc, cfg, "replicationdelay", 15));
+
+ final int poolSize = Math.max(0, getInt(rc, cfg, "threads", 1));
+ final String poolName = "ReplicateTo-" + rc.getName();
+ pool = workQueue.createQueue(poolSize, poolName);
+
+ String[] authGroupNames =
+ cfg.getStringList("remote", rc.getName(), "authGroup");
+ authEnabled = authGroupNames.length > 0;
+ Set<AccountGroup.Id> authGroups = groupsFor(db, authGroupNames);
+
+ final ReplicationUser remoteUser =
+ replicationUserFactory.create(authGroups);
+
+ projectControlFactory =
+ injector.createChildInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(CurrentUser.class).toInstance(remoteUser);
+ }
+ }).getInstance(ProjectControl.Factory.class);
+
+ opFactory = injector.createChildInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(PushReplication.ReplicationConfig.class).toInstance(
+ ReplicationConfig.this);
+ bind(RemoteConfig.class).toInstance(remote);
+ bind(PushOp.Factory.class).toProvider(
+ FactoryProvider.newFactory(PushOp.Factory.class, PushOp.class));
+ }
+ }).getInstance(PushOp.Factory.class);
+ }
+
+ private static Set<AccountGroup.Id> groupsFor(
+ SchemaFactory<ReviewDb> dbfactory, String[] groupNames) {
+ final Set<AccountGroup.Id> result = new HashSet<AccountGroup.Id>();
+ try {
+ final ReviewDb db = dbfactory.open();
+ try {
+ for (String name : groupNames) {
+ AccountGroup group =
+ db.accountGroups().get(new AccountGroup.NameKey(name));
+ if (group == null) {
+ log.warn("Group \"" + name + "\" not in database,"
+ + " removing from authGroup");
+ } else {
+ result.add(group.getId());
+ }
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ log.error("Database error: " + e);
+ }
+ return result;
+ }
+
+ private int getInt(final RemoteConfig rc, final Config cfg,
+ final String name, final int defValue) {
+ return cfg.getInt("remote", rc.getName(), name, defValue);
+ }
+
+ void schedule(final Project.NameKey project, final String ref,
+ final URIish uri) {
+ try {
+ if (authEnabled
+ && !projectControlFactory.controlFor(project).isVisible()) {
+ return;
+ }
+ } catch (NoSuchProjectException e1) {
+ log.error("Internal error: project " + project
+ + " not found during replication");
+ return;
+ }
+ synchronized (pending) {
+ PushOp e = pending.get(uri);
+ if (e == null) {
+ e = opFactory.create(project.get(), uri);
+ pool.schedule(e, delay, TimeUnit.SECONDS);
+ pending.put(uri, e);
+ }
+ e.addRef(ref);
+ }
+ }
+
+ void notifyStarting(final PushOp op) {
+ synchronized (pending) {
+ pending.remove(op.getURI());
+ }
+ }
+
+ boolean wouldPushRef(final String ref) {
+ for (final RefSpec s : remote.getPushRefSpecs()) {
+ if (s.matchSource(ref)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ List<URIish> getURIs(final Project.NameKey project, final String urlMatch) {
+ final List<URIish> r = new ArrayList<URIish>(remote.getURIs().size());
+ for (URIish uri : remote.getURIs()) {
+ if (matches(uri, urlMatch)) {
+ uri = uri.setPath(replace(uri.getPath(), "name", project.get()));
+ r.add(uri);
+ }
+ }
+ return r;
+ }
+
+ private boolean matches(URIish uri, final String urlMatch) {
+ if (urlMatch == null || urlMatch.equals("") || urlMatch.equals("*")) {
+ return true;
+ }
+ return uri.toString().contains(urlMatch);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReloadSubmitQueueOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReloadSubmitQueueOp.java
new file mode 100644
index 0000000000..356981d4db
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReloadSubmitQueueOp.java
@@ -0,0 +1,72 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+
+public class ReloadSubmitQueueOp extends DefaultQueueOp {
+ public interface Factory {
+ ReloadSubmitQueueOp create();
+ }
+
+ private static final Logger log =
+ LoggerFactory.getLogger(ReloadSubmitQueueOp.class);
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final MergeQueue mergeQueue;
+
+ @Inject
+ ReloadSubmitQueueOp(final WorkQueue wq, final SchemaFactory<ReviewDb> sf,
+ final MergeQueue mq) {
+ super(wq);
+ schema = sf;
+ mergeQueue = mq;
+ }
+
+ public void run() {
+ final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
+ try {
+ final ReviewDb c = schema.open();
+ try {
+ for (final Change change : c.changes().allSubmitted()) {
+ pending.add(change.getDest());
+ }
+ } finally {
+ c.close();
+ }
+ } catch (OrmException e) {
+ log.error("Cannot reload MergeQueue", e);
+ }
+
+ for (final Branch.NameKey branch : pending) {
+ mergeQueue.schedule(branch);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Reload Submit Queue";
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplicationQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplicationQueue.java
new file mode 100644
index 0000000000..d1c518d540
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplicationQueue.java
@@ -0,0 +1,59 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.gerrit.reviewdb.Project;
+
+/** Manages replication to other nodes. */
+public interface ReplicationQueue {
+ /** Is replication to one or more other destinations configured? */
+ boolean isEnabled();
+
+ /**
+ * Schedule a full replication for a single project.
+ * <p>
+ * All remote URLs are checked to verify the are current with regards to the
+ * local project state. If not, they are updated by pushing new refs, updating
+ * existing ones which don't match, and deleting stale refs which have been
+ * removed from the local repository.
+ *
+ * @param project identity of the project to replicate.
+ * @param urlMatch substring that must appear in a URI to support replication.
+ */
+ void scheduleFullSync(Project.NameKey project, String urlMatch);
+
+ /**
+ * Schedule update of a single ref.
+ * <p>
+ * This method automatically tries to batch together multiple requests in the
+ * same project, to take advantage of Git's native ability to update multiple
+ * refs during a single push operation.
+ *
+ * @param project identity of the project to replicate.
+ * @param ref unique name of the ref; must start with {@code refs/}.
+ */
+ void scheduleUpdate(Project.NameKey project, String ref);
+
+ /**
+ * Create new empty project at the remote sites.
+ * <p>
+ * When a new project has been created locally call this method to make sure
+ * that the project will be created at the remote sites as well.
+ *
+ * @param project of the project to be created.
+ * @param head name HEAD should point at (must be {@code refs/heads/...}).
+ */
+ void replicateNewProject(Project.NameKey project, String head);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
new file mode 100644
index 0000000000..51f5c99c22
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/WorkQueue.java
@@ -0,0 +1,245 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.git;
+
+import com.google.inject.Singleton;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RunnableScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Delayed execution of tasks using a background thread pool. */
+@Singleton
+public class WorkQueue {
+ private Executor defaultQueue;
+ private final CopyOnWriteArrayList<Executor> queues =
+ new CopyOnWriteArrayList<Executor>();
+
+ /** Get the default work queue, for miscellaneous tasks. */
+ public synchronized Executor getDefaultQueue() {
+ if (defaultQueue == null) {
+ defaultQueue = createQueue(1, "WorkQueue");
+ }
+ return defaultQueue;
+ }
+
+ /** Create a new executor queue with one thread. */
+ public Executor createQueue(final int poolsize, final String prefix) {
+ final Executor r = new Executor(poolsize, prefix);
+ r.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+ r.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+ queues.add(r);
+ return r;
+ }
+
+ /** Get all of the tasks currently scheduled in any work queue. */
+ public List<Task<?>> getTasks() {
+ final List<Task<?>> r = new ArrayList<Task<?>>();
+ for (final Executor e : queues) {
+ e.addAllTo(r);
+ }
+ return r;
+ }
+
+ /** Shutdown all queues, aborting any pending tasks that haven't started. */
+ public void shutdown() {
+ for (final Executor p : queues) {
+ p.shutdown();
+ boolean isTerminated;
+ do {
+ try {
+ isTerminated = p.awaitTermination(10, TimeUnit.SECONDS);
+ } catch (InterruptedException ie) {
+ isTerminated = false;
+ }
+ } while (!isTerminated);
+ }
+ queues.clear();
+ }
+
+ /** An isolated queue. */
+ public static class Executor extends ScheduledThreadPoolExecutor {
+ private final Set<Task<?>> active = new HashSet<Task<?>>();
+
+ Executor(final int corePoolSize, final String prefix) {
+ super(corePoolSize, new ThreadFactory() {
+ private final ThreadFactory parent = Executors.defaultThreadFactory();
+ private final AtomicInteger tid = new AtomicInteger(1);
+
+ @Override
+ public Thread newThread(final Runnable task) {
+ final Thread t = parent.newThread(task);
+ t.setName(prefix + "-thread-" + tid.getAndIncrement());
+ return t;
+ }
+ });
+ }
+
+ @Override
+ protected <V> RunnableScheduledFuture<V> decorateTask(
+ final Runnable runnable, final RunnableScheduledFuture<V> task) {
+ return new Task<V>(runnable, super.decorateTask(runnable, task));
+ }
+
+ @Override
+ protected <V> RunnableScheduledFuture<V> decorateTask(
+ final Callable<V> callable, final RunnableScheduledFuture<V> task) {
+ throw new UnsupportedOperationException("Callable not implemented");
+ }
+
+ @Override
+ protected void beforeExecute(Thread t, Runnable r) {
+ super.beforeExecute(t, r);
+ synchronized (active) {
+ active.add((Task<?>) r);
+ }
+ }
+
+ @Override
+ protected void afterExecute(Runnable r, Throwable t) {
+ super.afterExecute(r, t);
+ synchronized (active) {
+ active.remove(r);
+ }
+ }
+
+ void addAllTo(final List<Task<?>> list) {
+ synchronized (active) {
+ list.addAll(active);
+ }
+ for (final Runnable task : getQueue()) { // iterator is thread safe
+ list.add((Task<?>) task);
+ }
+ }
+ }
+
+ /** A wrapper around a scheduled Runnable, as maintained in the queue. */
+ public static class Task<V> implements RunnableScheduledFuture<V> {
+ /**
+ * Summarized status of a single task.
+ * <p>
+ * Tasks have the following state flow:
+ * <ol>
+ * <li>{@link #SLEEPING}: if scheduled with a non-zero delay.</li>
+ * <li>{@link #READY}: waiting for an available worker thread.</li>
+ * <li>{@link #RUNNING}: actively executing on a worker thread.</li>
+ * <li>{@link #DONE}: finished executing, if not periodic.</li>
+ * </ol>
+ */
+ public static enum State {
+ // Ordered like this so ordinal matches the order we would
+ // prefer to see tasks sorted in: done before running,
+ // running before ready, ready before sleeping.
+ //
+ DONE, CANCELLED, RUNNING, READY, SLEEPING, OTHER;
+ }
+
+ private final Runnable runnable;
+ private final RunnableScheduledFuture<V> task;
+ private volatile boolean running;
+
+ Task(Runnable runnable, RunnableScheduledFuture<V> task) {
+ this.runnable = runnable;
+ this.task = task;
+ }
+
+ /** Get the Runnable this task executes. */
+ public Runnable getRunnable() {
+ return runnable;
+ }
+
+ public State getState() {
+ if (isDone() && !isPeriodic()) {
+ return State.DONE;
+ } else if (isRunning()) {
+ return State.RUNNING;
+ } else if (isCancelled()) {
+ return State.CANCELLED;
+ }
+
+ final long delay = getDelay(TimeUnit.MILLISECONDS);
+ if (delay <= 0) {
+ return State.READY;
+ } else if (0 < delay) {
+ return State.SLEEPING;
+ }
+
+ return State.OTHER;
+ }
+
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return task.cancel(mayInterruptIfRunning);
+ }
+
+ public int compareTo(Delayed o) {
+ return task.compareTo(o);
+ }
+
+ public V get() throws InterruptedException, ExecutionException {
+ return task.get();
+ }
+
+ public V get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return task.get(timeout, unit);
+ }
+
+ public long getDelay(TimeUnit unit) {
+ return task.getDelay(unit);
+ }
+
+ public boolean isCancelled() {
+ return task.isCancelled();
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+ public boolean isDone() {
+ return task.isDone();
+ }
+
+ public boolean isPeriodic() {
+ return task.isPeriodic();
+ }
+
+ public void run() {
+ try {
+ running = true;
+ task.run();
+ } finally {
+ running = false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return runnable.toString();
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java b/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java
new file mode 100644
index 0000000000..650605fbd6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java
@@ -0,0 +1,175 @@
+// Copyright 2008 Google Inc. All rights reserved.
+// http://code.google.com/p/protobuf/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.gerrit.server.ioutil;
+
+import com.google.gerrit.reviewdb.CodedEnum;
+
+import org.eclipse.jgit.util.NB;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class BasicSerialization {
+ private static final byte[] NO_BYTES = {};
+
+ private static int safeRead(final InputStream input) throws IOException {
+ final int b = input.read();
+ if (b == -1) {
+ throw new EOFException();
+ }
+ return b;
+ }
+
+ /** Read a fixed-width 64 bit integer in network byte order (big-endian). */
+ public static long readFixInt64(final InputStream input) throws IOException {
+ final long h = readFixInt32(input);
+ final long l = readFixInt32(input) & 0xFFFFFFFFL;
+ return (h << 32) | l;
+ }
+
+ /** Write a fixed-width 64 bit integer in network byte order (big-endian). */
+ public static void writeFixInt64(final OutputStream output, final long val)
+ throws IOException {
+ writeFixInt32(output, (int) (val >>> 32));
+ writeFixInt32(output, (int) (val & 0xFFFFFFFFL));
+ }
+
+ /** Read a fixed-width 32 bit integer in network byte order (big-endian). */
+ public static int readFixInt32(final InputStream input) throws IOException {
+ final int b1 = safeRead(input);
+ final int b2 = safeRead(input);
+ final int b3 = safeRead(input);
+ final int b4 = safeRead(input);
+ return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
+ }
+
+ /** Write a fixed-width 32 bit integer in network byte order (big-endian). */
+ public static void writeFixInt32(final OutputStream output, final int val)
+ throws IOException {
+ output.write((val >>> 24) & 0xFF);
+ output.write((val >>> 16) & 0xFF);
+ output.write((val >>> 8) & 0xFF);
+ output.write(val & 0xFF);
+ }
+
+ /** Read a varint from the input, one byte at a time. */
+ public static int readVarInt32(final InputStream input) throws IOException {
+ int result = 0;
+ int offset = 0;
+ for (; offset < 32; offset += 7) {
+ final int b = safeRead(input);
+ result |= (b & 0x7f) << offset;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw new EOFException();
+ }
+
+ /** Write a varint; value is treated as an unsigned value. */
+ public static void writeVarInt32(final OutputStream output, int value)
+ throws IOException {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ output.write(value);
+ return;
+ } else {
+ output.write((value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ }
+
+ /** Read a fixed length byte array whose length is specified as a varint. */
+ public static byte[] readBytes(final InputStream input) throws IOException {
+ final int len = readVarInt32(input);
+ if (len == 0) {
+ return NO_BYTES;
+ }
+ final byte[] buf = new byte[len];
+ NB.readFully(input, buf, 0, len);
+ return buf;
+ }
+
+ /** Write a byte array prefixed by its length in a varint. */
+ public static void writeBytes(final OutputStream output, final byte[] data)
+ throws IOException {
+ writeBytes(output, data, 0, data.length);
+ }
+
+ /** Write a byte array prefixed by its length in a varint. */
+ public static void writeBytes(final OutputStream output, final byte[] data,
+ final int offset, final int len) throws IOException {
+ writeVarInt32(output, len);
+ output.write(data, offset, len);
+ }
+
+ /** Read a UTF-8 string, prefixed by its byte length in a varint. */
+ public static String readString(final InputStream input) throws IOException {
+ final byte[] bin = readBytes(input);
+ if (bin.length == 0) {
+ return null;
+ }
+ return new String(bin, 0, bin.length, "UTF-8");
+ }
+
+ /** Write a UTF-8 string, prefixed by its byte length in a varint. */
+ public static void writeString(final OutputStream output, final String s)
+ throws IOException {
+ if (s == null) {
+ writeVarInt32(output, 0);
+ } else {
+ writeBytes(output, s.getBytes("UTF-8"));
+ }
+ }
+
+ /** Read an enum whose code is stored as a varint. */
+ public static <T extends CodedEnum> T readEnum(final InputStream input,
+ final T[] all) throws IOException {
+ final int val = readVarInt32(input);
+ for (T t : all) {
+ if (t.getCode() == val) {
+ return t;
+ }
+ }
+ throw new IOException("Invalid enum " + val + " for " + all[0].getClass());
+ }
+
+ /** Write an enum whose code is stored as a varint. */
+ public static <T extends CodedEnum> void writeEnum(final OutputStream output,
+ final T e) throws IOException {
+ writeVarInt32(output, e.getCode());
+ }
+
+ private BasicSerialization() {
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
new file mode 100644
index 0000000000..bba69c88b3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** Send notice about a change being abandoned by its owner. */
+public class AbandonedSender extends ReplyToChangeSender {
+ public static interface Factory {
+ AbandonedSender create(Change change);
+ }
+
+ @Inject
+ public AbandonedSender(@Assisted Change c) {
+ super(c, "abandon");
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ ccAllApprovals();
+ bccStarredBy();
+ bccWatchesNotifyAllComments();
+ }
+
+ @Override
+ protected void format() {
+ appendText(getNameFor(fromId));
+ appendText(" has abandoned change " + change.getKey().abbreviate() + ":\n");
+ appendText("\n");
+ formatCoverLetter();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java
new file mode 100644
index 0000000000..c9a6d98411
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** Asks a user to review a change. */
+public class AddReviewerSender extends NewChangeSender {
+ public static interface Factory {
+ AddReviewerSender create(Change change);
+ }
+
+ @Inject
+ public AddReviewerSender(@Assisted Change c) {
+ super(c);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ ccExistingReviewers();
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/mail/Address.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/Address.java
index cc207e7a19..cc207e7a19 100644
--- a/src/main/java/com/google/gerrit/server/mail/Address.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/Address.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
new file mode 100644
index 0000000000..a010ac1576
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -0,0 +1,132 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchLineComment;
+import com.google.gerrit.server.patch.PatchFile;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/** Send comments, after the author of them hit used Publish Comments in the UI. */
+public class CommentSender extends ReplyToChangeSender {
+ public static interface Factory {
+ public CommentSender create(Change change);
+ }
+
+ private List<PatchLineComment> inlineComments = Collections.emptyList();
+
+ @Inject
+ public CommentSender(@Assisted Change c) {
+ super(c, "comment");
+ }
+
+ public void setPatchLineComments(final List<PatchLineComment> plc) {
+ inlineComments = plc;
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ ccAllApprovals();
+ bccStarredBy();
+ bccWatchesNotifyAllComments();
+ }
+
+ @Override
+ protected void format() {
+ if (!"".equals(getCoverLetter()) || !inlineComments.isEmpty()) {
+ appendText("Comments on Patch Set " + patchSet.getPatchSetId() + ":\n");
+ appendText("\n");
+ formatCoverLetter();
+ formatInlineComments();
+ if (getChangeUrl() != null) {
+ appendText("To respond, visit " + getChangeUrl() + "\n");
+ appendText("\n");
+ }
+ }
+ }
+
+ private void formatInlineComments() {
+ final Repository repo = getRepository();
+ try {
+ final PatchList patchList = repo != null ? getPatchList() : null;
+
+ Patch.Key currentFileKey = null;
+ PatchFile currentFileData = null;
+ for (final PatchLineComment c : inlineComments) {
+ final Patch.Key pk = c.getKey().getParentKey();
+ final int lineNbr = c.getLine();
+ final short side = c.getSide();
+
+ if (!pk.equals(currentFileKey)) {
+ appendText("....................................................\n");
+ appendText("File ");
+ appendText(pk.get());
+ appendText("\n");
+ currentFileKey = pk;
+
+ if (patchList != null) {
+ try {
+ currentFileData =
+ new PatchFile(repo, patchList, pk.getFileName());
+ } catch (IOException e) {
+ // Don't quote the line if we can't load it.
+ }
+ } else {
+ currentFileData = null;
+ }
+ }
+
+ appendText("Line " + lineNbr);
+ if (currentFileData != null) {
+ try {
+ final String lineStr = currentFileData.getLine(side, lineNbr);
+ appendText(": ");
+ appendText(lineStr);
+ } catch (Throwable cce) {
+ // Don't quote the line if we can't safely convert it.
+ }
+ }
+ appendText("\n");
+
+ appendText(c.getMessage().trim());
+ appendText("\n\n");
+ }
+ } finally {
+ if (repo != null) {
+ repo.close();
+ }
+ }
+ }
+
+ private Repository getRepository() {
+ try {
+ return server.openRepository(projectName);
+ } catch (RepositoryNotFoundException e) {
+ return null;
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
new file mode 100644
index 0000000000..d25d584149
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupMember;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Notify interested parties of a brand new change. */
+public class CreateChangeSender extends NewChangeSender {
+ public static interface Factory {
+ public CreateChangeSender create(Change change);
+ }
+
+ @Inject
+ public CreateChangeSender(@Assisted Change c) {
+ super(c);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ bccWatchers();
+ }
+
+ private void bccWatchers() {
+ if (db != null) {
+ try {
+ // BCC anyone else who has interest in this project's changes
+ //
+ final ProjectState ps = getProjectState();
+ if (ps != null) {
+ // Try to mark interested owners with a TO and not a BCC line.
+ //
+ final Set<Account.Id> owners = new HashSet<Account.Id>();
+ for (AccountGroup.Id g : getProjectOwners()) {
+ for (AccountGroupMember m : db.accountGroupMembers().byGroup(g)) {
+ owners.add(m.getAccountId());
+ }
+ }
+
+ // BCC anyone who has interest in this project's changes
+ //
+ for (AccountProjectWatch w : db.accountProjectWatches()
+ .notifyNewChanges(ps.getProject().getNameKey())) {
+ if (owners.contains(w.getAccountId())) {
+ add(RecipientType.TO, w.getAccountId());
+ } else {
+ add(RecipientType.BCC, w.getAccountId());
+ }
+ }
+ }
+ } catch (OrmException err) {
+ // Just don't CC everyone. Better to send a partial message to those
+ // we already have queued up then to fail deliver entirely to people
+ // who have a lower interest in the change.
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/mail/EmailException.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailException.java
index 7fe817a117..7fe817a117 100644
--- a/src/main/java/com/google/gerrit/server/mail/EmailException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailException.java
diff --git a/src/main/java/com/google/gerrit/server/mail/EmailHeader.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java
index 36a5f37c2f..36a5f37c2f 100644
--- a/src/main/java/com/google/gerrit/server/mail/EmailHeader.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailHeader.java
diff --git a/src/main/java/com/google/gerrit/server/mail/EmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailSender.java
index 411e6ca326..411e6ca326 100644
--- a/src/main/java/com/google/gerrit/server/mail/EmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/EmailSender.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java
new file mode 100644
index 0000000000..3090dc3914
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+
+/** Constructs an address to send email from. */
+public interface FromAddressGenerator {
+ public Address from(Account.Id fromId);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java
new file mode 100644
index 0000000000..d17bd994b7
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java
@@ -0,0 +1,136 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.ParamertizedString;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/** Creates a {@link FromAddressGenerator} from the {@link GerritServerConfig} */
+@Singleton
+public class FromAddressGeneratorProvider implements
+ Provider<FromAddressGenerator> {
+ private final FromAddressGenerator generator;
+
+ @Inject
+ FromAddressGeneratorProvider(@GerritServerConfig final Config cfg,
+ @GerritPersonIdent final PersonIdent myIdent,
+ final AccountCache accountCache) {
+
+ final String from = cfg.getString("sendemail", null, "from");
+ final Address srvAddr = toAddress(myIdent);
+
+ if (from == null || "MIXED".equalsIgnoreCase(from)) {
+ final String name = "${user} (Code Review)";
+ final String email = srvAddr.email;
+ generator = new PatternGen(srvAddr, accountCache, name, email);
+
+ } else if ("USER".equalsIgnoreCase(from)) {
+ generator = new UserGen(accountCache, srvAddr);
+
+ } else if ("SERVER".equalsIgnoreCase(from)) {
+ generator = new ServerGen(srvAddr);
+
+ } else {
+ final Address a = Address.parse(from);
+ generator = new PatternGen(srvAddr, accountCache, a.name, a.email);
+ }
+ }
+
+ private static Address toAddress(final PersonIdent myIdent) {
+ return new Address(myIdent.getName(), myIdent.getEmailAddress());
+ }
+
+ @Override
+ public FromAddressGenerator get() {
+ return generator;
+ }
+
+ static final class UserGen implements FromAddressGenerator {
+ private final AccountCache accountCache;
+ private final Address srvAddr;
+
+ UserGen(AccountCache accountCache, Address srvAddr) {
+ this.accountCache = accountCache;
+ this.srvAddr = srvAddr;
+ }
+
+ @Override
+ public Address from(final Account.Id fromId) {
+ if (fromId != null) {
+ final Account a = accountCache.get(fromId).getAccount();
+ if (a.getPreferredEmail() != null) {
+ return new Address(a.getFullName(), a.getPreferredEmail());
+ }
+ }
+ return srvAddr;
+ }
+ }
+
+ static final class ServerGen implements FromAddressGenerator {
+ private final Address srvAddr;
+
+ ServerGen(Address srvAddr) {
+ this.srvAddr = srvAddr;
+ }
+
+ @Override
+ public Address from(final Account.Id fromId) {
+ return srvAddr;
+ }
+ }
+
+ static final class PatternGen implements FromAddressGenerator {
+ private final String senderEmail;
+ private final Address serverAddress;
+ private final AccountCache accountCache;
+ private final ParamertizedString namePattern;
+
+ PatternGen(final Address serverAddress, final AccountCache accountCache,
+ final String namePattern, final String senderEmail) {
+ this.senderEmail = senderEmail;
+ this.serverAddress = serverAddress;
+ this.accountCache = accountCache;
+ this.namePattern = new ParamertizedString(namePattern);
+ }
+
+ @Override
+ public Address from(final Account.Id fromId) {
+ final String senderName;
+
+ if (fromId != null) {
+ final Account account = accountCache.get(fromId).getAccount();
+ String fullName = account.getFullName();
+ if (fullName == null || "".equals(fullName)) {
+ fullName = "Anonymous Coward";
+ }
+ senderName = namePattern.replace("user", fullName).toString();
+
+ } else {
+ senderName = serverAddress.name;
+ }
+
+ return new Address(senderName, senderEmail);
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java
new file mode 100644
index 0000000000..11a541ba04
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java
@@ -0,0 +1,52 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+/** Send notice about a change failing to merged. */
+public class MergeFailSender extends ReplyToChangeSender {
+ public static interface Factory {
+ public MergeFailSender create(Change change);
+ }
+
+ @Inject
+ public MergeFailSender(@Assisted Change c) {
+ super(c, "comment");
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ ccExistingReviewers();
+ }
+
+ @Override
+ protected void format() {
+ appendText("Change " + change.getKey().abbreviate());
+ if (patchSetInfo != null && patchSetInfo.getAuthor() != null
+ && patchSetInfo.getAuthor().getName() != null) {
+ appendText(" by ");
+ appendText(patchSetInfo.getAuthor().getName());
+ }
+ appendText(" FAILED to submit to ");
+ appendText(change.getDest().getShortName());
+ appendText(".\n\n");
+ formatCoverLetter();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
new file mode 100644
index 0000000000..a1f7d9e901
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MergedSender.java
@@ -0,0 +1,178 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Send notice about a change successfully merged. */
+public class MergedSender extends ReplyToChangeSender {
+ public static interface Factory {
+ public MergedSender create(Change change);
+ }
+
+ private Branch.NameKey dest;
+
+ @Inject
+ private ApprovalTypes approvalTypes;
+
+ @Inject
+ public MergedSender(@Assisted Change c) {
+ super(c, "merged");
+ dest = c.getDest();
+ }
+
+ public void setDest(final Branch.NameKey key) {
+ dest = key;
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ ccAllApprovals();
+ bccStarredBy();
+ bccWatchesNotifyAllComments();
+ bccWatchesNotifySubmittedChanges();
+ }
+
+ @Override
+ protected void format() {
+ appendText("Change " + change.getKey().abbreviate());
+ if (patchSetInfo != null && patchSetInfo.getAuthor() != null
+ && patchSetInfo.getAuthor().getName() != null) {
+ appendText(" by ");
+ appendText(patchSetInfo.getAuthor().getName());
+ }
+ appendText(" submitted to ");
+ appendText(dest.getShortName());
+ appendText(":\n\n");
+ formatChangeDetail();
+ formatApprovals();
+ }
+
+ private void formatApprovals() {
+ if (db != null && patchSet != null) {
+ try {
+ final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> pos =
+ new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>();
+
+ final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> neg =
+ new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>();
+
+ for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(
+ patchSet.getId())) {
+ if (ca.getValue() > 0) {
+ insert(pos, ca);
+ } else if (ca.getValue() < 0) {
+ insert(neg, ca);
+ }
+ }
+
+ format("Approvals", pos);
+ format("Objections", neg);
+ } catch (OrmException err) {
+ // Don't list the approvals
+ }
+ }
+ }
+
+ private void format(final String type,
+ final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list) {
+ if (list.isEmpty()) {
+ return;
+ }
+ appendText(type + ":\n");
+ for (final Map.Entry<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> ent : list
+ .entrySet()) {
+ final Map<ApprovalCategory.Id, PatchSetApproval> l = ent.getValue();
+ appendText(" ");
+ appendText(getNameFor(ent.getKey()));
+ appendText(": ");
+ boolean first = true;
+ for (ApprovalType at : approvalTypes.getApprovalTypes()) {
+ final PatchSetApproval ca = l.get(at.getCategory().getId());
+ if (ca == null) {
+ continue;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ appendText("; ");
+ }
+
+ final ApprovalCategoryValue v = at.getValue(ca);
+ if (v != null) {
+ appendText(v.getName());
+ } else {
+ appendText(at.getCategory().getName());
+ appendText("=");
+ if (ca.getValue() > 0) {
+ appendText("+");
+ }
+ appendText("" + ca.getValue());
+ }
+ }
+ appendText("\n");
+ }
+ appendText("\n");
+ }
+
+ private void insert(
+ final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list,
+ final PatchSetApproval ca) {
+ Map<ApprovalCategory.Id, PatchSetApproval> m = list.get(ca.getAccountId());
+ if (m == null) {
+ m = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
+ list.put(ca.getAccountId(), m);
+ }
+ m.put(ca.getCategoryId(), ca);
+ }
+
+ private void bccWatchesNotifySubmittedChanges() {
+ if (db != null) {
+ try {
+ // BCC anyone else who has interest in this project's changes
+ //
+ final ProjectState ps = getProjectState();
+ if (ps != null) {
+ for (AccountProjectWatch w : db.accountProjectWatches()
+ .notifySubmittedChanges(ps.getProject().getNameKey())) {
+ add(RecipientType.BCC, w.getAccountId());
+ }
+ }
+ } catch (OrmException err) {
+ // Just don't CC everyone. Better to send a partial message to those
+ // we already have queued up then to fail deliver entirely to people
+ // who have a lower interest in the change.
+ }
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
new file mode 100644
index 0000000000..5e38740b07
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
@@ -0,0 +1,137 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.inject.Inject;
+
+import com.jcraft.jsch.HostKey;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/** Sends an email alerting a user to a new change for them to review. */
+public abstract class NewChangeSender extends OutgoingEmail {
+ @Inject
+ private SshInfo sshInfo;
+
+ private final Set<Account.Id> reviewers = new HashSet<Account.Id>();
+ private final Set<Account.Id> extraCC = new HashSet<Account.Id>();
+
+ protected NewChangeSender(Change c) {
+ super(c, "newchange");
+ }
+
+ public void addReviewers(final Collection<Account.Id> cc) {
+ reviewers.addAll(cc);
+ }
+
+ public void addExtraCC(final Collection<Account.Id> cc) {
+ extraCC.addAll(cc);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ setHeader("Message-ID", getChangeMessageThreadId());
+
+ add(RecipientType.TO, reviewers);
+ add(RecipientType.CC, extraCC);
+ rcptToAuthors(RecipientType.CC);
+ }
+
+ @Override
+ protected void format() {
+ formatSalutation();
+ formatChangeDetail();
+
+ appendText("\n");
+ appendText(" " + getPullUrl() + "\n");
+ }
+
+ private void formatSalutation() {
+ final String changeUrl = getChangeUrl();
+
+ if (reviewers.isEmpty()) {
+ formatDest();
+ if (changeUrl != null) {
+ appendText("\n");
+ appendText(" " + changeUrl + "\n");
+ appendText("\n");
+ }
+ appendText("\n");
+
+ } else {
+ appendText("Hello");
+ for (final Iterator<Account.Id> i = reviewers.iterator(); i.hasNext();) {
+ appendText(" ");
+ appendText(getNameFor(i.next()));
+ appendText(",");
+ }
+ appendText("\n");
+ appendText("\n");
+
+ appendText("I'd like you to do a code review.");
+ if (changeUrl != null) {
+ appendText(" Please visit\n");
+ appendText("\n");
+ appendText(" " + changeUrl + "\n");
+ appendText("\n");
+ appendText("to review the following change:\n");
+ }
+ appendText("\n");
+
+ formatDest();
+ appendText("\n");
+ }
+ }
+
+ private void formatDest() {
+ appendText("Change " + change.getKey().abbreviate());
+ appendText(" for ");
+ appendText(change.getDest().getShortName());
+ appendText(" in ");
+ appendText(projectName);
+ appendText(":\n");
+ }
+
+ private String getPullUrl() {
+ final List<HostKey> hostKeys = sshInfo.getHostKeys();
+ if (hostKeys.isEmpty()) {
+ return "";
+ }
+
+ final String host = hostKeys.get(0).getHost();
+ final StringBuilder r = new StringBuilder();
+ r.append("git pull ssh://");
+ if (host.startsWith("*:")) {
+ r.append(getGerritHost());
+ r.append(host.substring(2));
+ } else {
+ r.append(host);
+ }
+ r.append("/");
+ r.append(projectName);
+ r.append(" ");
+ r.append(patchSet.getRefName());
+ return r.toString();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
new file mode 100644
index 0000000000..c49c556edb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
@@ -0,0 +1,632 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountProjectWatch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.StarredChange;
+import com.google.gerrit.reviewdb.UserIdentity;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.patch.PatchList;
+import com.google.gerrit.server.patch.PatchListCache;
+import com.google.gerrit.server.patch.PatchListEntry;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.util.SystemReader;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/** Sends an email to one or more interested parties. */
+public abstract class OutgoingEmail {
+ private static final String HDR_TO = "To";
+ private static final String HDR_CC = "CC";
+
+ private static final Random RNG = new Random();
+ private final String messageClass;
+ protected final Change change;
+ protected String projectName;
+ private final HashSet<Account.Id> rcptTo = new HashSet<Account.Id>();
+ private final Map<String, EmailHeader> headers;
+ private final List<Address> smtpRcptTo = new ArrayList<Address>();
+ private Address smtpFromAddress;
+ private StringBuilder body;
+ private boolean inFooter;
+
+ protected Account.Id fromId;
+ protected PatchSet patchSet;
+ protected PatchSetInfo patchSetInfo;
+ protected ChangeMessage changeMessage;
+ protected ReviewDb db;
+
+ @Inject
+ protected GitRepositoryManager server;
+
+ @Inject
+ private ProjectCache projectCache;
+
+ @Inject
+ private AccountCache accountCache;
+
+ @Inject
+ private PatchListCache patchListCache;
+
+ @Inject
+ private FromAddressGenerator fromAddressGenerator;
+
+ @Inject
+ private EmailSender emailSender;
+
+ @Inject
+ private PatchSetInfoFactory patchSetInfoFactory;
+
+ @Inject
+ private IdentifiedUser.GenericFactory identifiedUserFactory;
+
+ @Inject
+ @CanonicalWebUrl
+ @Nullable
+ private Provider<String> urlProvider;
+
+ private ProjectState projectState;
+
+ protected OutgoingEmail(final Change c, final String mc) {
+ change = c;
+ messageClass = mc;
+ headers = new LinkedHashMap<String, EmailHeader>();
+ }
+
+ protected OutgoingEmail(final String mc) {
+ this(null, mc);
+ }
+
+ public void setFrom(final Account.Id id) {
+ fromId = id;
+ }
+
+ public void setPatchSet(final PatchSet ps) {
+ patchSet = ps;
+ }
+
+ public void setPatchSet(final PatchSet ps, final PatchSetInfo psi) {
+ patchSet = ps;
+ patchSetInfo = psi;
+ }
+
+ public void setChangeMessage(final ChangeMessage cm) {
+ changeMessage = cm;
+ }
+
+ public void setReviewDb(final ReviewDb d) {
+ db = d;
+ }
+
+ /**
+ * Format and enqueue the message for delivery.
+ *
+ * @throws EmailException
+ */
+ public void send() throws EmailException {
+ if (!emailSender.isEnabled()) {
+ // Server has explicitly disabled email sending.
+ //
+ return;
+ }
+
+ init();
+ format();
+ if (shouldSendMessage()) {
+ if (fromId != null) {
+ // If we are impersonating a user, make sure they receive a CC of
+ // this message so they can always review and audit what we sent
+ // on their behalf to others.
+ //
+ add(RecipientType.CC, fromId);
+ }
+ if (change != null) {
+ if (getChangeUrl() != null) {
+ openFooter();
+ appendText("To view visit ");
+ appendText(getChangeUrl());
+ appendText("\n");
+ }
+ if (getSettingsUrl() != null) {
+ openFooter();
+ appendText("To unsubscribe, visit ");
+ appendText(getSettingsUrl());
+ appendText("\n");
+ }
+
+ if (inFooter) {
+ appendText("\n");
+ } else {
+ openFooter();
+ }
+ appendText("Gerrit-MessageType: " + messageClass + "\n");
+ appendText("Gerrit-Project: " + projectName + "\n");
+ appendText("Gerrit-Branch: " + change.getDest().getShortName() + "\n");
+ }
+
+ if (headers.get("Message-ID").isEmpty()) {
+ final StringBuilder rndid = new StringBuilder();
+ rndid.append("<");
+ rndid.append(System.currentTimeMillis());
+ rndid.append("-");
+ rndid.append(Integer.toString(RNG.nextInt(999999), 36));
+ rndid.append("@");
+ rndid.append(SystemReader.getInstance().getHostname());
+ rndid.append(">");
+ setHeader("Message-ID", rndid.toString());
+ }
+
+ emailSender.send(smtpFromAddress, smtpRcptTo, headers, body.toString());
+ }
+ }
+
+ /** Format the message body by calling {@link #appendText(String)}. */
+ protected abstract void format();
+
+ /** Setup the message headers and envelope (TO, CC, BCC). */
+ protected void init() {
+ if (change != null && projectCache != null) {
+ projectState = projectCache.get(change.getProject());
+ projectName =
+ projectState != null ? projectState.getProject().getName() : null;
+ } else {
+ projectState = null;
+ projectName = null;
+ }
+
+ smtpFromAddress = fromAddressGenerator.from(fromId);
+ if (changeMessage != null && changeMessage.getWrittenOn() != null) {
+ setHeader("Date", new Date(changeMessage.getWrittenOn().getTime()));
+ } else {
+ setHeader("Date", new Date());
+ }
+ headers.put("From", new EmailHeader.AddressList(smtpFromAddress));
+ headers.put(HDR_TO, new EmailHeader.AddressList());
+ headers.put(HDR_CC, new EmailHeader.AddressList());
+ if (change != null) {
+ setChangeSubjectHeader();
+ }
+ setHeader("Message-ID", "");
+
+ if (fromId != null) {
+ // If we have a user that this message is supposedly caused by
+ // but the From header on the email does not match the user as
+ // it is a generic header for this Gerrit server, include the
+ // Reply-To header with the current user's email address.
+ //
+ final Address a = toAddress(fromId);
+ if (a != null && !smtpFromAddress.email.equals(a.email)) {
+ setHeader("Reply-To", a.email);
+ }
+ }
+
+ setHeader("X-Gerrit-MessageType", messageClass);
+ if (change != null) {
+ setHeader("X-Gerrit-Change-Id", "" + change.getKey().get());
+ setListIdHeader();
+ setChangeUrlHeader();
+ setCommitIdHeader();
+ }
+ body = new StringBuilder();
+ inFooter = false;
+
+ if (change != null && db != null) {
+ if (patchSet == null) {
+ try {
+ patchSet = db.patchSets().get(change.currentPatchSetId());
+ } catch (OrmException err) {
+ patchSet = null;
+ }
+ }
+
+ if (patchSet != null && patchSetInfo == null) {
+ try {
+ patchSetInfo = patchSetInfoFactory.get(patchSet.getId());
+ } catch (PatchSetInfoNotAvailableException err) {
+ patchSetInfo = null;
+ }
+ }
+ }
+ }
+
+ private void setListIdHeader() {
+ // Set a reasonable list id so that filters can be used to sort messages
+ //
+ final StringBuilder listid = new StringBuilder();
+ listid.append("gerrit-");
+ listid.append(projectName.replace('/', '-'));
+ listid.append("@");
+ listid.append(getGerritHost());
+
+ final String listidStr = listid.toString();
+ setHeader("Mailing-List", "list " + listidStr);
+ setHeader("List-Id", "<" + listidStr.replace('@', '.') + ">");
+ if (getSettingsUrl() != null) {
+ setHeader("List-Unsubscribe", "<" + getSettingsUrl() + ">");
+ }
+ }
+
+ private void setChangeUrlHeader() {
+ final String u = getChangeUrl();
+ if (u != null) {
+ setHeader("X-Gerrit-ChangeURL", "<" + u + ">");
+ }
+ }
+
+ private void setCommitIdHeader() {
+ if (patchSet != null && patchSet.getRevision() != null
+ && patchSet.getRevision().get() != null
+ && patchSet.getRevision().get().length() > 0) {
+ setHeader("X-Gerrit-Commit", patchSet.getRevision().get());
+ }
+ }
+
+ private void setChangeSubjectHeader() {
+ final StringBuilder subj = new StringBuilder();
+ subj.append("[");
+ subj.append(change.getDest().getShortName());
+ subj.append("] ");
+ subj.append("Change ");
+ subj.append(change.getKey().abbreviate());
+ subj.append(": (");
+ subj.append(projectName);
+ subj.append(") ");
+ if (change.getSubject().length() > 60) {
+ subj.append(change.getSubject().substring(0, 60));
+ subj.append("...");
+ } else {
+ subj.append(change.getSubject());
+ }
+ setHeader("Subject", subj.toString());
+ }
+
+ protected String getGerritHost() {
+ if (getGerritUrl() != null) {
+ try {
+ return new URL(getGerritUrl()).getHost();
+ } catch (MalformedURLException e) {
+ // Try something else.
+ }
+ }
+
+ // Fall back onto whatever the local operating system thinks
+ // this server is called. We hopefully didn't get here as a
+ // good admin would have configured the canonical url.
+ //
+ return SystemReader.getInstance().getHostname();
+ }
+
+ /** Get a link to the change; null if the server doesn't know its own address. */
+ protected String getChangeUrl() {
+ if (change != null && getGerritUrl() != null) {
+ final StringBuilder r = new StringBuilder();
+ r.append(getGerritUrl());
+ r.append(change.getChangeId());
+ return r.toString();
+ }
+ return null;
+ }
+
+ private String getSettingsUrl() {
+ if (getGerritUrl() != null) {
+ final StringBuilder r = new StringBuilder();
+ r.append(getGerritUrl());
+ r.append("settings");
+ return r.toString();
+ }
+ return null;
+ }
+
+ protected String getGerritUrl() {
+ return urlProvider.get();
+ }
+
+ protected String getChangeMessageThreadId() {
+ final StringBuilder r = new StringBuilder();
+ r.append('<');
+ r.append("gerrit");
+ r.append('.');
+ r.append(change.getCreatedOn().getTime());
+ r.append('.');
+ r.append(change.getKey().get());
+ r.append('@');
+ r.append(getGerritHost());
+ r.append('>');
+ return r.toString();
+ }
+
+ /** Set a header in the outgoing message. */
+ protected void setHeader(final String name, final String value) {
+ headers.put(name, new EmailHeader.String(value));
+ }
+
+ protected void setHeader(final String name, final Date date) {
+ headers.put(name, new EmailHeader.Date(date));
+ }
+
+ /** Append text to the outgoing email body. */
+ protected void appendText(final String text) {
+ if (text != null) {
+ body.append(text);
+ }
+ }
+
+ private void openFooter() {
+ if (!inFooter) {
+ inFooter = true;
+ appendText("-- \n");
+ }
+ }
+
+ /** Format the sender's "cover letter", {@link #getCoverLetter()}. */
+ protected void formatCoverLetter() {
+ final String cover = getCoverLetter();
+ if (!"".equals(cover)) {
+ appendText(cover);
+ appendText("\n\n");
+ }
+ }
+
+ /** Get the text of the "cover letter", from {@link ChangeMessage}. */
+ protected String getCoverLetter() {
+ if (changeMessage != null) {
+ final String txt = changeMessage.getMessage();
+ if (txt != null) {
+ return txt.trim();
+ }
+ }
+ return "";
+ }
+
+ /** Format the change message and the affected file list. */
+ protected void formatChangeDetail() {
+ if (patchSetInfo != null) {
+ appendText(patchSetInfo.getMessage().trim());
+ appendText("\n");
+ } else {
+ appendText(change.getSubject().trim());
+ appendText("\n");
+ }
+
+ if (patchSet != null) {
+ appendText("---\n");
+ for (PatchListEntry p : getPatchList().getPatches()) {
+ appendText(p.getChangeType().getCode() + " " + p.getNewName() + "\n");
+ }
+ appendText("\n");
+ }
+ }
+
+ /** Get the patch list corresponding to this patch set. */
+ protected PatchList getPatchList() {
+ if (patchSet != null) {
+ return patchListCache.get(change, patchSet);
+ }
+ return null;
+ }
+
+ /** Lookup a human readable name for an account, usually the "full name". */
+ protected String getNameFor(final Account.Id accountId) {
+ if (accountId == null) {
+ return "Anonymous Coward";
+ }
+
+ final Account userAccount = accountCache.get(accountId).getAccount();
+ String name = userAccount.getFullName();
+ if (name == null) {
+ name = userAccount.getPreferredEmail();
+ }
+ if (name == null) {
+ name = "Anonymous Coward #" + accountId;
+ }
+ return name;
+ }
+
+ protected boolean shouldSendMessage() {
+ if (body.length() == 0) {
+ // If we have no message body, don't send.
+ //
+ return false;
+ }
+
+ if (rcptTo.isEmpty()) {
+ // If we have nobody to send this message to, then all of our
+ // selection filters previously for this type of message were
+ // unable to match a destination. Don't bother sending it.
+ //
+ return false;
+ }
+
+ if (rcptTo.size() == 1 && rcptTo.contains(fromId)) {
+ // If the only recipient is also the sender, don't bother.
+ //
+ return false;
+ }
+
+ return true;
+ }
+
+ /** Get the project entity the change is in; null if its been deleted. */
+ protected ProjectState getProjectState() {
+ return projectState;
+ }
+
+ /** Get the groups which own the project. */
+ protected Set<AccountGroup.Id> getProjectOwners() {
+ final ProjectState r;
+
+ r = projectCache.get(change.getProject());
+ return r != null ? r.getOwners() : Collections.<AccountGroup.Id> emptySet();
+ }
+
+ /** Schedule this message for delivery to the listed accounts. */
+ protected void add(final RecipientType rt, final Collection<Account.Id> list) {
+ for (final Account.Id id : list) {
+ add(rt, id);
+ }
+ }
+
+ /** TO or CC all vested parties (change owner, patch set uploader, author). */
+ protected void rcptToAuthors(final RecipientType rt) {
+ add(rt, change.getOwner());
+ if (patchSet != null) {
+ add(rt, patchSet.getUploader());
+ }
+ if (patchSetInfo != null) {
+ add(rt, patchSetInfo.getAuthor());
+ add(rt, patchSetInfo.getCommitter());
+ }
+ }
+
+ private void add(final RecipientType rt, final UserIdentity who) {
+ if (who != null && who.getAccount() != null) {
+ add(rt, who.getAccount());
+ }
+ }
+
+ /** BCC any user who has starred this change. */
+ protected void bccStarredBy() {
+ if (db != null) {
+ try {
+ // BCC anyone who has starred this change.
+ //
+ for (StarredChange w : db.starredChanges().byChange(change.getId())) {
+ add(RecipientType.BCC, w.getAccountId());
+ }
+ } catch (OrmException err) {
+ // Just don't BCC everyone. Better to send a partial message to those
+ // we already have queued up then to fail deliver entirely to people
+ // who have a lower interest in the change.
+ }
+ }
+ }
+
+ /** BCC any user who has set "notify all comments" on this project. */
+ protected void bccWatchesNotifyAllComments() {
+ if (db != null) {
+ try {
+ // BCC anyone else who has interest in this project's changes
+ //
+ final ProjectState ps = getProjectState();
+ if (ps != null) {
+ for (final AccountProjectWatch w : db.accountProjectWatches()
+ .notifyAllComments(ps.getProject().getNameKey())) {
+ add(RecipientType.BCC, w.getAccountId());
+ }
+ }
+ } catch (OrmException err) {
+ // Just don't CC everyone. Better to send a partial message to those
+ // we already have queued up then to fail deliver entirely to people
+ // who have a lower interest in the change.
+ }
+ }
+ }
+
+ /** Any user who has published comments on this change. */
+ protected void ccAllApprovals() {
+ ccApprovals(true);
+ }
+
+ /** Users who have non-zero approval codes on the change. */
+ protected void ccExistingReviewers() {
+ ccApprovals(false);
+ }
+
+ private void ccApprovals(final boolean includeZero) {
+ if (db != null) {
+ try {
+ // CC anyone else who has posted an approval mark on this change
+ //
+ for (PatchSetApproval ap : db.patchSetApprovals().byChange(
+ change.getId())) {
+ if (!includeZero && ap.getValue() == 0) {
+ continue;
+ }
+ add(RecipientType.CC, ap.getAccountId());
+ }
+ } catch (OrmException err) {
+ }
+ }
+ }
+
+ /** Schedule delivery of this message to the given account. */
+ protected void add(final RecipientType rt, final Account.Id to) {
+ if (!rcptTo.contains(to) && isVisibleTo(to)) {
+ rcptTo.add(to);
+ add(rt, toAddress(to));
+ }
+ }
+
+ private boolean isVisibleTo(final Account.Id to) {
+ return projectState == null
+ || change == null
+ || projectState.controlFor(identifiedUserFactory.create(to))
+ .controlFor(change).isVisible();
+ }
+
+ /** Schedule delivery of this message to the given account. */
+ protected void add(final RecipientType rt, final Address addr) {
+ if (addr != null && addr.email != null && addr.email.length() > 0) {
+ smtpRcptTo.add(addr);
+ switch (rt) {
+ case TO:
+ ((EmailHeader.AddressList) headers.get(HDR_TO)).add(addr);
+ break;
+ case CC:
+ ((EmailHeader.AddressList) headers.get(HDR_CC)).add(addr);
+ break;
+ }
+ }
+ }
+
+ private Address toAddress(final Account.Id id) {
+ final Account a = accountCache.get(id).getAccount();
+ final String e = a.getPreferredEmail();
+ if (e == null) {
+ return null;
+ }
+ return new Address(a.getFullName(), e);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/mail/RecipientType.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RecipientType.java
index ea0def0abf..ea0def0abf 100644
--- a/src/main/java/com/google/gerrit/server/mail/RecipientType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RecipientType.java
diff --git a/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
index d22cc5902f..d22cc5902f 100644
--- a/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/RegisterNewEmailSender.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
new file mode 100644
index 0000000000..69b446b28a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
@@ -0,0 +1,149 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import com.jcraft.jsch.HostKey;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/** Send notice of new patch sets for reviewers. */
+public class ReplacePatchSetSender extends ReplyToChangeSender {
+ public static interface Factory {
+ public ReplacePatchSetSender create(Change change);
+ }
+
+ @Inject
+ private SshInfo sshInfo;
+
+ private final Set<Account.Id> reviewers = new HashSet<Account.Id>();
+ private final Set<Account.Id> extraCC = new HashSet<Account.Id>();
+
+ @Inject
+ public ReplacePatchSetSender(@Assisted Change c) {
+ super(c, "newpatchset");
+ }
+
+ public void addReviewers(final Collection<Account.Id> cc) {
+ reviewers.addAll(cc);
+ }
+
+ public void addExtraCC(final Collection<Account.Id> cc) {
+ extraCC.addAll(cc);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ if (fromId != null) {
+ // Don't call yourself a reviewer of your own patch set.
+ //
+ reviewers.remove(fromId);
+ }
+ add(RecipientType.TO, reviewers);
+ add(RecipientType.CC, extraCC);
+ rcptToAuthors(RecipientType.CC);
+ }
+
+ @Override
+ protected void format() {
+ formatSalutation();
+ formatChangeDetail();
+
+ appendText("\n");
+ appendText(" " + getPullUrl() + "\n");
+ }
+
+ private void formatSalutation() {
+ final String changeUrl = getChangeUrl();
+
+ if (reviewers.isEmpty()) {
+ formatDest();
+ if (changeUrl != null) {
+ appendText("\n");
+ appendText(" " + changeUrl + "\n");
+ appendText("\n");
+ }
+ appendText("\n");
+
+ } else {
+ appendText("Hello");
+ for (final Iterator<Account.Id> i = reviewers.iterator(); i.hasNext();) {
+ appendText(" ");
+ appendText(getNameFor(i.next()));
+ appendText(",");
+ }
+ appendText("\n");
+ appendText("\n");
+
+ appendText("I'd like you to reexamine change "
+ + change.getKey().abbreviate() + ".");
+ if (changeUrl != null) {
+ appendText(" Please visit\n");
+ appendText("\n");
+ appendText(" " + changeUrl + "\n");
+ appendText("\n");
+ appendText("to look at patch set " + patchSet.getPatchSetId());
+ appendText(":\n");
+ }
+ appendText("\n");
+
+ formatDest();
+ appendText("\n");
+ }
+ }
+
+ private void formatDest() {
+ appendText("Change " + change.getKey().abbreviate());
+ appendText(" (patch set " + patchSet.getPatchSetId() + ")");
+ appendText(" for ");
+ appendText(change.getDest().getShortName());
+ appendText(" in ");
+ appendText(projectName);
+ appendText(":\n");
+ }
+
+ private String getPullUrl() {
+ final List<HostKey> hostKeys = sshInfo.getHostKeys();
+ if (hostKeys.isEmpty()) {
+ return "";
+ }
+
+ final String host = hostKeys.get(0).getHost();
+ final StringBuilder r = new StringBuilder();
+ r.append("git pull ssh://");
+ if (host.startsWith("*:")) {
+ r.append(getGerritHost());
+ r.append(host.substring(2));
+ } else {
+ r.append(host);
+ }
+ r.append("/");
+ r.append(projectName);
+ r.append(" ");
+ r.append(patchSet.getRefName());
+ return r.toString();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
new file mode 100644
index 0000000000..99e9565e42
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.reviewdb.Change;
+
+/** Alert a user to a reply to a change, usually commentary made during review. */
+public abstract class ReplyToChangeSender extends OutgoingEmail {
+ protected ReplyToChangeSender(Change c, String mc) {
+ super(c, mc);
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ final String threadId = getChangeMessageThreadId();
+ setHeader("In-Reply-To", threadId);
+ setHeader("References", threadId);
+
+ rcptToAuthors(RecipientType.TO);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
new file mode 100644
index 0000000000..1fb31a037b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
@@ -0,0 +1,213 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import com.google.gerrit.common.Version;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.apache.commons.net.smtp.AuthSMTPClient;
+import org.apache.commons.net.smtp.SMTPClient;
+import org.apache.commons.net.smtp.SMTPReply;
+import org.eclipse.jgit.lib.Config;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/** Sends email via a nearby SMTP server. */
+@Singleton
+public class SmtpEmailSender implements EmailSender {
+ public static enum Encryption {
+ NONE, SSL, TLS;
+ }
+
+ private final boolean enabled;
+
+ private String smtpHost;
+ private int smtpPort;
+ private String smtpUser;
+ private String smtpPass;
+ private Encryption smtpEncryption;
+ private boolean sslVerify;
+ private String[] allowrcpt;
+
+ @Inject
+ SmtpEmailSender(@GerritServerConfig final Config cfg) {
+ enabled = cfg.getBoolean("sendemail", null, "enable", true);
+
+ smtpHost = cfg.getString("sendemail", null, "smtpserver");
+ if (smtpHost == null) {
+ smtpHost = "127.0.0.1";
+ }
+
+ smtpEncryption =
+ ConfigUtil.getEnum(cfg, "sendemail", null, "smtpencryption",
+ Encryption.NONE);
+ sslVerify = cfg.getBoolean("sendemail", null, "sslverify", true);
+
+ final int defaultPort;
+ switch (smtpEncryption) {
+ case SSL:
+ defaultPort = 465;
+ break;
+
+ case NONE:
+ case TLS:
+ default:
+ defaultPort = 25;
+ break;
+ }
+ smtpPort = cfg.getInt("sendemail", null, "smtpserverport", defaultPort);
+
+ smtpUser = cfg.getString("sendemail", null, "smtpuser");
+ smtpPass = cfg.getString("sendemail", null, "smtppass");
+ allowrcpt = cfg.getStringList("sendemail", null, "allowrcpt");
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public void send(final Address from, final Collection<Address> rcpt,
+ final Map<String, EmailHeader> callerHeaders, final String body)
+ throws EmailException {
+ if (!isEnabled()) {
+ throw new EmailException("Sending email is disabled");
+ }
+
+ final Map<String, EmailHeader> hdrs =
+ new LinkedHashMap<String, EmailHeader>(callerHeaders);
+ setMissingHeader(hdrs, "MIME-Version", "1.0");
+ setMissingHeader(hdrs, "Content-Type", "text/plain; charset=UTF-8");
+ setMissingHeader(hdrs, "Content-Transfer-Encoding", "8bit");
+ setMissingHeader(hdrs, "Content-Disposition", "inline");
+ setMissingHeader(hdrs, "User-Agent", "Gerrit/" + Version.getVersion());
+
+ try {
+ final SMTPClient client = open();
+ try {
+ if (!client.setSender(from.email)) {
+ throw new EmailException("Server " + smtpHost
+ + " rejected from address " + from.email);
+ }
+
+ for (Address addr : rcpt) {
+ if (!client.addRecipient(addr.email)) {
+ String error = client.getReplyString();
+ throw new EmailException("Server " + smtpHost
+ + " rejected recipient " + addr + ": " + error);
+ }
+ }
+
+ Writer w = client.sendMessageData();
+ if (w == null) {
+ throw new EmailException("Server " + smtpHost + " rejected body");
+ }
+ w = new BufferedWriter(w);
+
+ for (Map.Entry<String, EmailHeader> h : hdrs.entrySet()) {
+ if (!h.getValue().isEmpty()) {
+ w.write(h.getKey());
+ w.write(": ");
+ h.getValue().write(w);
+ w.write("\r\n");
+ }
+ }
+
+ w.write("\r\n");
+ w.write(body);
+ w.flush();
+ w.close();
+
+ if (!client.completePendingCommand()) {
+ throw new EmailException("Server " + smtpHost + " rejected body");
+ }
+
+ client.logout();
+ } finally {
+ client.disconnect();
+ }
+ } catch (IOException e) {
+ throw new EmailException("Cannot send outgoing email", e);
+ }
+ }
+
+ private void setMissingHeader(final Map<String, EmailHeader> hdrs,
+ final String name, final String value) {
+ if (!hdrs.containsKey(name) || hdrs.get(name).isEmpty()) {
+ hdrs.put(name, new EmailHeader.String(value));
+ }
+ }
+
+ private SMTPClient open() throws EmailException {
+ final AuthSMTPClient client = new AuthSMTPClient("UTF-8");
+ client.setAllowRcpt(allowrcpt);
+
+ if (smtpEncryption == Encryption.SSL) {
+ client.enableSSL(sslVerify);
+ }
+
+ try {
+ client.connect(smtpHost, smtpPort);
+ if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
+ throw new EmailException("SMTP server rejected connection");
+ }
+ if (!client.login()) {
+ String e = client.getReplyString();
+ throw new EmailException("SMTP server rejected login: " + e);
+ }
+
+ if (smtpEncryption == Encryption.TLS) {
+ if (!client.startTLS(smtpHost, smtpPort, sslVerify)) {
+ throw new EmailException("SMTP server does not support TLS");
+ }
+ if (!client.login()) {
+ String e = client.getReplyString();
+ throw new EmailException("SMTP server rejected login: " + e);
+ }
+ }
+
+ if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
+ String e = client.getReplyString();
+ throw new EmailException("SMTP server rejected auth: " + e);
+ }
+ } catch (IOException e) {
+ if (client.isConnected()) {
+ try {
+ client.disconnect();
+ } catch (IOException e2) {
+ }
+ }
+ throw new EmailException(e.getMessage(), e);
+ } catch (EmailException e) {
+ if (client.isConnected()) {
+ try {
+ client.disconnect();
+ } catch (IOException e2) {
+ }
+ }
+ throw e;
+ }
+ return client;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java
new file mode 100644
index 0000000000..6c751dca46
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchFile.java
@@ -0,0 +1,115 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import com.google.gerrit.common.errors.CorruptEntityException;
+import com.google.gerrit.common.errors.NoSuchEntityException;
+import com.google.gerrit.reviewdb.Patch;
+
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+
+import java.io.IOException;
+import java.nio.charset.CharacterCodingException;
+
+/** State supporting processing of a single {@link Patch} instance. */
+public class PatchFile {
+ private final Repository repo;
+ private final PatchListEntry entry;
+ private final RevTree aTree;
+ private final RevTree bTree;
+
+ private Text a;
+ private Text b;
+
+ public PatchFile(final Repository repo, final PatchList patchList,
+ final String fileName) throws MissingObjectException,
+ IncorrectObjectTypeException, IOException {
+ this.repo = repo;
+ this.entry = patchList.get(fileName);
+
+ final RevWalk rw = new RevWalk(repo);
+ final RevCommit bCommit = rw.parseCommit(patchList.getNewId());
+ if (patchList.getOldId() != null) {
+ aTree = rw.parseTree(patchList.getOldId());
+ } else {
+ final RevCommit p = bCommit.getParent(0);
+ rw.parseHeaders(p);
+ aTree = p.getTree();
+ }
+ bTree = bCommit.getTree();
+ }
+
+ /**
+ * Extract a line from the file, as a string.
+ *
+ * @param file the file index to extract.
+ * @param line the line number to extract (1 based; 1 is the first line).
+ * @return the string version of the file line.
+ * @throws CorruptEntityException the patch cannot be read.
+ * @throws IOException the patch or complete file content cannot be read.
+ * @throws NoSuchEntityException
+ * @throws CharacterCodingException the file is not a known character set.
+ */
+ public String getLine(final int file, final int line)
+ throws CorruptEntityException, IOException, NoSuchEntityException {
+ switch (file) {
+ case 0:
+ if (a == null) {
+ a = load(aTree, entry.getOldName());
+ }
+ return a.getLine(line - 1);
+
+ case 1:
+ if (b == null) {
+ b = load(bTree, entry.getNewName());
+ }
+ return b.getLine(line - 1);
+
+ default:
+ throw new NoSuchEntityException();
+ }
+ }
+
+ private Text load(final ObjectId tree, final String path)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ CorruptObjectException, IOException {
+ if (path == null) {
+ return Text.EMPTY;
+ }
+ final TreeWalk tw = TreeWalk.forPath(repo, path, tree);
+ if (tw == null) {
+ return Text.EMPTY;
+ }
+ if (tw.getFileMode(0).getObjectType() != Constants.OBJ_BLOB) {
+ return Text.EMPTY;
+ }
+ final ObjectId id = tw.getObjectId(0);
+ final ObjectLoader ldr = repo.openObject(id);
+ if (ldr == null) {
+ throw new MissingObjectException(id, Constants.TYPE_BLOB);
+ }
+ return new Text(ldr.getCachedBytes());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java
new file mode 100644
index 0000000000..b45fb83a5e
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchList.java
@@ -0,0 +1,164 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+
+import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readCanBeNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeCanBeNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
+
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.server.config.Nullable;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+public class PatchList implements Serializable {
+ private static final long serialVersionUID = PatchListKey.serialVersionUID;
+ private static final Comparator<PatchListEntry> PATCH_CMP =
+ new Comparator<PatchListEntry>() {
+ @Override
+ public int compare(final PatchListEntry a, final PatchListEntry b) {
+ return a.getNewName().compareTo(b.getNewName());
+ }
+ };
+
+ @Nullable
+ private transient ObjectId oldId;
+ private transient ObjectId newId;
+ private transient PatchListEntry[] patches;
+
+ PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId,
+ final PatchListEntry[] patches) {
+ this.oldId = oldId != null ? oldId.copy() : null;
+ this.newId = newId.copy();
+
+ Arrays.sort(patches, PATCH_CMP);
+ this.patches = patches;
+ }
+
+ /** Old side tree or commit; null only if this is a combined diff. */
+ @Nullable
+ public ObjectId getOldId() {
+ return oldId;
+ }
+
+ /** New side commit. */
+ public ObjectId getNewId() {
+ return newId;
+ }
+
+ /** Get a sorted, unmodifiable list of all files in this list. */
+ public List<PatchListEntry> getPatches() {
+ return Collections.unmodifiableList(Arrays.asList(patches));
+ }
+
+ /**
+ * Get a sorted, modifiable list of all files in this list.
+ * <p>
+ * The returned list items do not populate:
+ * <ul>
+ * <li>{@link Patch#getCommentCount()}
+ * <li>{@link Patch#getDraftCount()}
+ * <li>{@link Patch#isReviewedByCurrentUser()}
+ * </ul>
+ *
+ * @param setId the patch set identity these patches belong to. This really
+ * should not need to be specified, but is a current legacy artifact of
+ * how the cache is keyed versus how the database is keyed.
+ */
+ public List<Patch> toPatchList(final PatchSet.Id setId) {
+ final ArrayList<Patch> r = new ArrayList<Patch>(patches.length);
+ for (final PatchListEntry e : patches) {
+ r.add(e.toPatch(setId));
+ }
+ return r;
+ }
+
+ /** Find an entry by name, returning an empty entry if not present. */
+ public PatchListEntry get(final String fileName) {
+ final int index = search(fileName);
+ return 0 <= index ? patches[index] : PatchListEntry.empty(fileName);
+ }
+
+ private int search(final String fileName) {
+ int high = patches.length;
+ int low = 0;
+ while (low < high) {
+ final int mid = (low + high) >>> 1;
+ final int cmp = patches[mid].getNewName().compareTo(fileName);
+ if (cmp < 0)
+ low = mid + 1;
+ else if (cmp == 0)
+ return mid;
+ else
+ high = mid;
+ }
+ return -(low + 1);
+ }
+
+ private void writeObject(final ObjectOutputStream output) throws IOException {
+ final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ final DeflaterOutputStream out = new DeflaterOutputStream(buf);
+ try {
+ writeCanBeNull(out, oldId);
+ writeNotNull(out, newId);
+ writeVarInt32(out, patches.length);
+ for (PatchListEntry p : patches) {
+ p.writeTo(out);
+ }
+ } finally {
+ out.close();
+ }
+ writeBytes(output, buf.toByteArray());
+ }
+
+ private void readObject(final ObjectInputStream input) throws IOException {
+ final ByteArrayInputStream buf = new ByteArrayInputStream(readBytes(input));
+ final InflaterInputStream in = new InflaterInputStream(buf);
+ try {
+ oldId = readCanBeNull(in);
+ newId = readNotNull(in);
+ final int cnt = readVarInt32(in);
+ final PatchListEntry[] all = new PatchListEntry[cnt];
+ for (int i = 0; i < all.length; i++) {
+ all[i] = PatchListEntry.readFrom(in);
+ }
+ patches = all;
+ } finally {
+ in.close();
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
new file mode 100644
index 0000000000..209d0b74d4
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+
+/** Provides a cached list of {@link PatchListEntry}. */
+public interface PatchListCache {
+ public PatchList get(PatchListKey key);
+
+ public PatchList get(Change change, PatchSet patchSet);
+
+ public PatchList get(Change change, PatchSet patchSet, Whitespace whitespace);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
new file mode 100644
index 0000000000..71342ff6f4
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
@@ -0,0 +1,200 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import static com.google.gerrit.common.data.PatchScriptSettings.Whitespace.IGNORE_NONE;
+
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.EvictionPolicy;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Provides a cached list of {@link PatchListEntry}. */
+@Singleton
+public class PatchListCacheImpl implements PatchListCache {
+ private static final String CACHE_NAME = "diff";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<PatchListKey, PatchList>> type =
+ new TypeLiteral<Cache<PatchListKey, PatchList>>() {};
+ disk(type, CACHE_NAME) //
+ .memoryLimit(128) // very large items, cache only a few
+ .evictionPolicy(EvictionPolicy.LRU) // prefer most recent
+ ;
+ bind(PatchListCacheImpl.class);
+ bind(PatchListCache.class).to(PatchListCacheImpl.class);
+ }
+ };
+ }
+
+ private final GitRepositoryManager repoManager;
+ private final SelfPopulatingCache<PatchListKey, PatchList> self;
+
+ @Inject
+ PatchListCacheImpl(final GitRepositoryManager grm,
+ @Named(CACHE_NAME) final Cache<PatchListKey, PatchList> raw) {
+ repoManager = grm;
+ self = new SelfPopulatingCache<PatchListKey, PatchList>(raw) {
+ @Override
+ protected PatchList createEntry(final PatchListKey key) throws Exception {
+ return compute(key);
+ }
+ };
+ }
+
+ public PatchList get(final PatchListKey key) {
+ return self.get(key);
+ }
+
+ public PatchList get(final Change change, final PatchSet patchSet) {
+ return get(change, patchSet, IGNORE_NONE);
+ }
+
+ public PatchList get(final Change change, final PatchSet patchSet,
+ final Whitespace whitespace) {
+ final Project.NameKey projectKey = change.getProject();
+ final ObjectId a = null;
+ final ObjectId b = ObjectId.fromString(patchSet.getRevision().get());
+ return get(new PatchListKey(projectKey, a, b, whitespace));
+ }
+
+ private PatchList compute(final PatchListKey key)
+ throws MissingObjectException, IncorrectObjectTypeException, IOException {
+ final Repository repo = repoManager.openRepository(key.projectKey.get());
+ try {
+ return readPatchList(key, repo);
+ } finally {
+ repo.close();
+ }
+ }
+
+ private PatchList readPatchList(final PatchListKey key, final Repository repo)
+ throws IOException {
+ final RevCommit b = new RevWalk(repo).parseCommit(key.getNewId());
+ final AnyObjectId a = aFor(key, repo, b);
+
+ final List<String> args = new ArrayList<String>();
+ args.add("git");
+ args.add("--git-dir=.");
+ args.add("diff-tree");
+ args.add("-M");
+ switch (key.getWhitespace()) {
+ case IGNORE_NONE:
+ break;
+ case IGNORE_SPACE_AT_EOL:
+ args.add("--ignore-space-at-eol");
+ break;
+ case IGNORE_SPACE_CHANGE:
+ args.add("--ignore-space-change");
+ break;
+ case IGNORE_ALL_SPACE:
+ args.add("--ignore-all-space");
+ break;
+ default:
+ throw new IOException("Unsupported whitespace " + key.getWhitespace());
+ }
+ if (a == null /* want combined diff */) {
+ args.add("--cc");
+ args.add(b.name());
+ } else {
+ args.add("--unified=1");
+ args.add(a.name());
+ args.add(b.name());
+ }
+
+ final org.eclipse.jgit.patch.Patch p = new org.eclipse.jgit.patch.Patch();
+ final Process diffProcess = exec(repo, args);
+ try {
+ diffProcess.getOutputStream().close();
+ diffProcess.getErrorStream().close();
+
+ final InputStream in = diffProcess.getInputStream();
+ try {
+ p.parse(in);
+ } finally {
+ in.close();
+ }
+ } finally {
+ try {
+ final int rc = diffProcess.waitFor();
+ if (rc != 0) {
+ throw new IOException("git diff-tree exited abnormally: " + rc);
+ }
+ } catch (InterruptedException ie) {
+ }
+ }
+
+ final int cnt = p.getFiles().size();
+ final PatchListEntry[] entries = new PatchListEntry[cnt];
+ for (int i = 0; i < cnt; i++) {
+ entries[i] = new PatchListEntry(p.getFiles().get(i));
+ }
+ return new PatchList(a, b, entries);
+ }
+
+ private static AnyObjectId aFor(final PatchListKey key,
+ final Repository repo, final RevCommit b) throws IOException {
+ if (key.getOldId() != null) {
+ return key.getOldId();
+ }
+
+ switch (b.getParentCount()) {
+ case 0:
+ return emptyTree(repo);
+ case 1:
+ return b.getParent(0);
+ default:
+ // merge commit, return null to force combined diff behavior
+ return null;
+ }
+ }
+
+ private static Process exec(final Repository repo, final List<String> args)
+ throws IOException {
+ final String[] argv = args.toArray(new String[args.size()]);
+ return Runtime.getRuntime().exec(argv, null, repo.getDirectory());
+ }
+
+ private static ObjectId emptyTree(final Repository repo) throws IOException {
+ return new ObjectWriter(repo).writeCanonicalTree(new byte[0]);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
new file mode 100644
index 0000000000..7e9e89725a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
@@ -0,0 +1,263 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
+import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
+
+import com.google.gerrit.reviewdb.Patch;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.Patch.ChangeType;
+import com.google.gerrit.reviewdb.Patch.PatchType;
+
+import org.eclipse.jgit.diff.Edit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.patch.CombinedFileHeader;
+import org.eclipse.jgit.patch.FileHeader;
+import org.eclipse.jgit.util.IntList;
+import org.eclipse.jgit.util.RawParseUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class PatchListEntry {
+ private static final byte[] EMPTY_HEADER = {};
+
+ static PatchListEntry empty(final String fileName) {
+ return new PatchListEntry(ChangeType.MODIFIED, PatchType.UNIFIED, null,
+ fileName, EMPTY_HEADER, Collections.<Edit> emptyList());
+ }
+
+ private final ChangeType changeType;
+ private final PatchType patchType;
+ private final String oldName;
+ private final String newName;
+ private final byte[] header;
+ private final List<Edit> edits;
+
+ PatchListEntry(final FileHeader hdr) {
+ changeType = toChangeType(hdr);
+ patchType = toPatchType(hdr);
+
+ switch (changeType) {
+ case DELETED:
+ oldName = null;
+ newName = hdr.getOldName();
+ break;
+
+ case ADDED:
+ case MODIFIED:
+ oldName = null;
+ newName = hdr.getNewName();
+ break;
+
+ case COPIED:
+ case RENAMED:
+ oldName = hdr.getOldName();
+ newName = hdr.getNewName();
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unsupported type " + changeType);
+ }
+
+ header = compact(hdr);
+
+ if (hdr instanceof CombinedFileHeader
+ || hdr.getHunks().isEmpty() //
+ || hdr.getOldMode() == FileMode.GITLINK
+ || hdr.getNewMode() == FileMode.GITLINK) {
+ edits = Collections.emptyList();
+ } else {
+ edits = Collections.unmodifiableList(hdr.toEditList());
+ }
+ }
+
+ private PatchListEntry(final ChangeType changeType,
+ final PatchType patchType, final String oldName, final String newName,
+ final byte[] header, final List<Edit> edits) {
+ this.changeType = changeType;
+ this.patchType = patchType;
+ this.oldName = oldName;
+ this.newName = newName;
+ this.header = header;
+ this.edits = edits;
+ }
+
+ public ChangeType getChangeType() {
+ return changeType;
+ }
+
+ public PatchType getPatchType() {
+ return patchType;
+ }
+
+ public String getOldName() {
+ return oldName;
+ }
+
+ public String getNewName() {
+ return newName;
+ }
+
+ public List<Edit> getEdits() {
+ return edits;
+ }
+
+ public List<String> getHeaderLines() {
+ final IntList m = RawParseUtils.lineMap(header, 0, header.length);
+ final List<String> headerLines = new ArrayList<String>(m.size() - 1);
+ for (int i = 1; i < m.size() - 1; i++) {
+ final int b = m.get(i);
+ final int e = m.get(i + 1);
+ headerLines.add(RawParseUtils.decode(Constants.CHARSET, header, b, e));
+ }
+ return headerLines;
+ }
+
+ Patch toPatch(final PatchSet.Id setId) {
+ final Patch p = new Patch(new Patch.Key(setId, getNewName()));
+ p.setChangeType(getChangeType());
+ p.setPatchType(getPatchType());
+ p.setSourceFileName(getOldName());
+ return p;
+ }
+
+ void writeTo(final OutputStream out) throws IOException {
+ writeEnum(out, changeType);
+ writeEnum(out, patchType);
+ writeString(out, oldName);
+ writeString(out, newName);
+ writeBytes(out, header);
+
+ writeVarInt32(out, edits.size());
+ for (final Edit e : edits) {
+ writeVarInt32(out, e.getBeginA());
+ writeVarInt32(out, e.getEndA());
+ writeVarInt32(out, e.getBeginB());
+ writeVarInt32(out, e.getEndB());
+ }
+ }
+
+ static PatchListEntry readFrom(final InputStream in) throws IOException {
+ final ChangeType changeType = readEnum(in, ChangeType.values());
+ final PatchType patchType = readEnum(in, PatchType.values());
+ final String oldName = readString(in);
+ final String newName = readString(in);
+ final byte[] hdr = readBytes(in);
+
+ final int editCount = readVarInt32(in);
+ final Edit[] editArray = new Edit[editCount];
+ for (int i = 0; i < editCount; i++) {
+ final int beginA = readVarInt32(in);
+ final int endA = readVarInt32(in);
+ final int beginB = readVarInt32(in);
+ final int endB = readVarInt32(in);
+ editArray[i] = new Edit(beginA, endA, beginB, endB);
+ }
+
+ return new PatchListEntry(changeType, patchType, oldName, newName, hdr,
+ Collections.unmodifiableList(Arrays.asList(editArray)));
+ }
+
+ private static byte[] compact(final FileHeader h) {
+ final int end = end(h);
+ if (h.getStartOffset() == 0 && end == h.getBuffer().length) {
+ return h.getBuffer();
+ }
+
+ final byte[] buf = new byte[end - h.getStartOffset()];
+ System.arraycopy(h.getBuffer(), h.getStartOffset(), buf, 0, buf.length);
+ return buf;
+ }
+
+ private static int end(final FileHeader h) {
+ if (h instanceof CombinedFileHeader) {
+ return h.getEndOffset();
+ }
+ if (!h.getHunks().isEmpty()) {
+ return h.getHunks().get(0).getStartOffset();
+ }
+ return h.getEndOffset();
+ }
+
+ private static ChangeType toChangeType(final FileHeader hdr) {
+ switch (hdr.getChangeType()) {
+ case ADD:
+ return Patch.ChangeType.ADDED;
+ case MODIFY:
+ return Patch.ChangeType.MODIFIED;
+ case DELETE:
+ return Patch.ChangeType.DELETED;
+ case RENAME:
+ return Patch.ChangeType.RENAMED;
+ case COPY:
+ return Patch.ChangeType.COPIED;
+ default:
+ throw new IllegalArgumentException("Unsupported type "
+ + hdr.getChangeType());
+ }
+ }
+
+ private static PatchType toPatchType(final FileHeader hdr) {
+ PatchType pt;
+
+ if (hdr instanceof CombinedFileHeader) {
+ pt = Patch.PatchType.N_WAY;
+ } else {
+ switch (hdr.getPatchType()) {
+ case UNIFIED:
+ pt = Patch.PatchType.UNIFIED;
+ break;
+ case GIT_BINARY:
+ case BINARY:
+ pt = Patch.PatchType.BINARY;
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported type "
+ + hdr.getPatchType());
+ }
+ }
+
+ if (pt != PatchType.BINARY) {
+ final byte[] buf = hdr.getBuffer();
+ for (int ptr = hdr.getStartOffset(); ptr < hdr.getEndOffset(); ptr++) {
+ if (buf[ptr] == '\0') {
+ // Its really binary, but Git couldn't see the nul early enough
+ // to realize its binary, and instead produced the diff.
+ //
+ // Force it to be a binary; it really should have been that.
+ //
+ pt = PatchType.BINARY;
+ break;
+ }
+ }
+ }
+
+ return pt;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
new file mode 100644
index 0000000000..a6c0a72ffb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
@@ -0,0 +1,111 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
+import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readCanBeNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeCanBeNull;
+import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
+
+import com.google.gerrit.common.data.PatchScriptSettings.Whitespace;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.config.Nullable;
+
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+public class PatchListKey implements Serializable {
+ static final long serialVersionUID = 9L;
+
+ private transient ObjectId oldId;
+ private transient ObjectId newId;
+ private transient Whitespace whitespace;
+
+ transient Project.NameKey projectKey; // not required to form the key
+
+ public PatchListKey(final Project.NameKey pk, final AnyObjectId a,
+ final AnyObjectId b, final Whitespace ws) {
+ projectKey = pk;
+ oldId = a != null ? a.copy() : null;
+ newId = b.copy();
+ whitespace = ws;
+ }
+
+ /** Old side commit, or null to assume ancestor or combined merge. */
+ @Nullable
+ public ObjectId getOldId() {
+ return oldId;
+ }
+
+ /** New side commit name. */
+ public ObjectId getNewId() {
+ return newId;
+ }
+
+ public Whitespace getWhitespace() {
+ return whitespace;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+
+ if (oldId != null) {
+ h = h * 31 + oldId.hashCode();
+ }
+
+ h = h * 31 + newId.hashCode();
+ h = h * 31 + whitespace.name().hashCode();
+
+ return h;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o instanceof PatchListKey) {
+ final PatchListKey k = (PatchListKey) o;
+ return eq(oldId, k.oldId) //
+ && eq(newId, k.newId) //
+ && whitespace == k.whitespace;
+ }
+ return false;
+ }
+
+ private static boolean eq(final ObjectId a, final ObjectId b) {
+ if (a == null && b == null) {
+ return true;
+ }
+ return a != null && b != null && AnyObjectId.equals(a, b);
+ }
+
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ writeCanBeNull(out, oldId);
+ writeNotNull(out, newId);
+ writeEnum(out, whitespace);
+ }
+
+ private void readObject(final ObjectInputStream in) throws IOException {
+ oldId = readCanBeNull(in);
+ newId = readNotNull(in);
+ whitespace = readEnum(in, Whitespace.values());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
new file mode 100644
index 0000000000..961717297b
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
@@ -0,0 +1,117 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.UserIdentity;
+import com.google.gerrit.server.account.AccountByEmailCache;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.util.Set;
+
+
+/**
+ * Factory class creating PatchSetInfo from meta-data found in Git repository.
+ */
+@Singleton
+public class PatchSetInfoFactory {
+ private final GitRepositoryManager repoManager;
+ private final SchemaFactory<ReviewDb> schemaFactory;
+ private final AccountByEmailCache byEmailCache;
+
+ @Inject
+ public PatchSetInfoFactory(final GitRepositoryManager grm,
+ final SchemaFactory<ReviewDb> schemaFactory,
+ final AccountByEmailCache byEmailCache) {
+ this.repoManager = grm;
+ this.schemaFactory = schemaFactory;
+ this.byEmailCache = byEmailCache;
+ }
+
+ public PatchSetInfo get(RevCommit src, PatchSet.Id psi) {
+ PatchSetInfo info = new PatchSetInfo(psi);
+ info.setSubject(src.getShortMessage());
+ info.setMessage(src.getFullMessage());
+ info.setAuthor(toUserIdentity(src.getAuthorIdent()));
+ info.setCommitter(toUserIdentity(src.getCommitterIdent()));
+
+ return info;
+ }
+
+ public PatchSetInfo get(PatchSet.Id patchSetId)
+ throws PatchSetInfoNotAvailableException {
+ ReviewDb db = null;
+ Repository repo = null;
+ try {
+ db = schemaFactory.open();
+ final PatchSet patchSet = db.patchSets().get(patchSetId);
+ final Change change = db.changes().get(patchSet.getId().getParentKey());
+ final Project.NameKey projectKey = change.getProject();
+ final String projectName = projectKey.get();
+ repo = repoManager.openRepository(projectName);
+ final RevWalk rw = new RevWalk(repo);
+ final RevCommit src =
+ rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
+ return get(src, patchSetId);
+ } catch (OrmException e) {
+ throw new PatchSetInfoNotAvailableException(e);
+ } catch (IOException e) {
+ throw new PatchSetInfoNotAvailableException(e);
+ } finally {
+ if (db != null) {
+ db.close();
+ }
+ if (repo != null) {
+ repo.close();
+ }
+ }
+ }
+
+ private UserIdentity toUserIdentity(final PersonIdent who) {
+ final UserIdentity u = new UserIdentity();
+ u.setName(who.getName());
+ u.setEmail(who.getEmailAddress());
+ u.setDate(new Timestamp(who.getWhen().getTime()));
+ u.setTimeZone(who.getTimeZoneOffset());
+
+ // If only one account has access to this email address, select it
+ // as the identity of the user.
+ //
+ final Set<Account.Id> a = byEmailCache.get(u.getEmail());
+ if (a.size() == 1) {
+ u.setAccount(a.iterator().next());
+ }
+
+ return u;
+ }
+
+}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java
index 621f614254..621f614254 100644
--- a/src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoNotAvailableException.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java
new file mode 100644
index 0000000000..4ce13338ab
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/Text.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import com.google.gerrit.common.data.SparseFileContent;
+
+import org.eclipse.jgit.diff.RawText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.util.RawParseUtils;
+
+public class Text extends RawText {
+ public static final Text EMPTY = new Text(new byte[0]);
+
+ public Text(final byte[] r) {
+ super(r);
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ public String getLine(final int i) {
+ final int s = lines.get(i + 1);
+ int e = lines.get(i + 2);
+ if (content[e - 1] == '\n') {
+ e--;
+ }
+ return RawParseUtils.decode(Constants.CHARSET, content, s, e);
+ }
+
+ public void addLineTo(final SparseFileContent out, final int i) {
+ out.addLine(i, getLine(i));
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
new file mode 100644
index 0000000000..fd6fc85d1c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -0,0 +1,134 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+/** Access control management for a user accessing a single change. */
+public class ChangeControl {
+ public static class Factory {
+ private final ProjectControl.Factory projectControl;
+ private final Provider<ReviewDb> db;
+
+ @Inject
+ Factory(final ProjectControl.Factory p, final Provider<ReviewDb> d) {
+ projectControl = p;
+ db = d;
+ }
+
+ public ChangeControl controlFor(final Change.Id id)
+ throws NoSuchChangeException {
+ final Change change;
+ try {
+ change = db.get().changes().get(id);
+ if (change == null) {
+ throw new NoSuchChangeException(id);
+ }
+ } catch (OrmException e) {
+ throw new NoSuchChangeException(id, e);
+ }
+ return controlFor(change);
+ }
+
+ public ChangeControl controlFor(final Change change)
+ throws NoSuchChangeException {
+ try {
+ final Project.NameKey projectKey = change.getProject();
+ return projectControl.validateFor(projectKey).controlFor(change);
+ } catch (NoSuchProjectException e) {
+ throw new NoSuchChangeException(change.getId(), e);
+ }
+ }
+
+ public ChangeControl validateFor(final Change.Id id)
+ throws NoSuchChangeException {
+ return validate(controlFor(id));
+ }
+
+ public ChangeControl validateFor(final Change change)
+ throws NoSuchChangeException {
+ return validate(controlFor(change));
+ }
+
+ private static ChangeControl validate(final ChangeControl c)
+ throws NoSuchChangeException {
+ if (!c.isVisible()) {
+ throw new NoSuchChangeException(c.getChange().getId());
+ }
+ return c;
+ }
+ }
+
+ private final ProjectControl projectControl;
+ private final Change change;
+
+ ChangeControl(final ProjectControl p, final Change c) {
+ this.projectControl = p;
+ this.change = c;
+ }
+
+ public ChangeControl forAnonymousUser() {
+ return new ChangeControl(projectControl.forAnonymousUser(), change);
+ }
+
+ public ChangeControl forUser(final CurrentUser who) {
+ return new ChangeControl(projectControl.forUser(who), change);
+ }
+
+ public CurrentUser getCurrentUser() {
+ return getProjectControl().getCurrentUser();
+ }
+
+ public ProjectControl getProjectControl() {
+ return projectControl;
+ }
+
+ public Project getProject() {
+ return getProjectControl().getProject();
+ }
+
+ public Change getChange() {
+ return change;
+ }
+
+ /** Can this user see this change? */
+ public boolean isVisible() {
+ return getProjectControl().isVisible();
+ }
+
+ /** Can this user abandon this change? */
+ public boolean canAbandon() {
+ return isOwner() // owner (aka creator) of the change can abandon
+ || getProjectControl().isOwner() // project owner can abandon
+ || getCurrentUser().isAdministrator() // site administers are god
+ ;
+ }
+
+ /** Is this user the owner of the change? */
+ public boolean isOwner() {
+ if (getCurrentUser() instanceof IdentifiedUser) {
+ final IdentifiedUser i = (IdentifiedUser) getCurrentUser();
+ return i.getAccountId().equals(change.getOwner());
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java
new file mode 100644
index 0000000000..0985bfb729
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.Change;
+
+/** Indicates the change does not exist. */
+public class NoSuchChangeException extends Exception {
+ public NoSuchChangeException(final Change.Id key) {
+ this(key, null);
+ }
+
+ public NoSuchChangeException(final Change.Id key, final Throwable why) {
+ super(key.toString(), why);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java
new file mode 100644
index 0000000000..fca9840c44
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.Project;
+
+/** Indicates the project does not exist. */
+public class NoSuchProjectException extends Exception {
+ public NoSuchProjectException(final Project.NameKey key) {
+ this(key, null);
+ }
+
+ public NoSuchProjectException(final Project.NameKey key, final Throwable why) {
+ super(key.toString(), why);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
new file mode 100644
index 0000000000..b5353a54d5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -0,0 +1,31 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.Project;
+
+/** Cache of project information, including access rights. */
+public interface ProjectCache {
+ /**
+ * Get the cached data for a project by its unique name.
+ *
+ * @param projectName name of the project.
+ * @return the cached data; null if no such project exists.
+ */
+ public ProjectState get(Project.NameKey projectName);
+
+ /** Invalidate the cached information about the given project. */
+ public void evict(Project p);
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
new file mode 100644
index 0000000000..389415ac4d
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/** Cache of project information, including access rights. */
+@Singleton
+public class ProjectCacheImpl implements ProjectCache {
+ private static final String CACHE_NAME = "projects";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<Project.NameKey, ProjectState>> type =
+ new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {};
+ core(type, CACHE_NAME);
+ bind(ProjectCacheImpl.class);
+ bind(ProjectCache.class).to(ProjectCacheImpl.class);
+ }
+ };
+ }
+
+ private final ProjectState.Factory projectStateFactory;
+ private final Project.NameKey wildProject;
+ private final ProjectState.InheritedRights inheritedRights;
+ private final SchemaFactory<ReviewDb> schema;
+ private final SelfPopulatingCache<Project.NameKey, ProjectState> byName;
+
+ @Inject
+ ProjectCacheImpl(final ProjectState.Factory psf,
+ final SchemaFactory<ReviewDb> sf,
+ @WildProjectName final Project.NameKey wp,
+ @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName) {
+ projectStateFactory = psf;
+ schema = sf;
+ wildProject = wp;
+
+ this.byName =
+ new SelfPopulatingCache<Project.NameKey, ProjectState>(byName) {
+ @Override
+ public ProjectState createEntry(final Project.NameKey key)
+ throws Exception {
+ return lookup(key);
+ }
+ };
+
+ this.inheritedRights = new ProjectState.InheritedRights() {
+ @Override
+ public Collection<ProjectRight> get() {
+ return ProjectCacheImpl.this.get(wildProject).getLocalRights();
+ }
+ };
+ }
+
+ private ProjectState lookup(final Project.NameKey key) throws OrmException {
+ final ReviewDb db = schema.open();
+ try {
+ final Project p = db.projects().get(key);
+ if (p == null) {
+ return null;
+ }
+
+ final Collection<ProjectRight> rights =
+ Collections.unmodifiableCollection(db.projectRights().byProject(
+ p.getNameKey()).toList());
+
+ return projectStateFactory.create(p, rights, inheritedRights);
+ } finally {
+ db.close();
+ }
+ }
+
+ /**
+ * Get the cached data for a project by its unique name.
+ *
+ * @param projectName name of the project.
+ * @return the cached data; null if no such project exists.
+ */
+ public ProjectState get(final Project.NameKey projectName) {
+ return byName.get(projectName);
+ }
+
+ /** Invalidate the cached information about the given project. */
+ public void evict(final Project p) {
+ if (p != null) {
+ byName.remove(p.getNameKey());
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
new file mode 100644
index 0000000000..c3e54c9cf6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -0,0 +1,227 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_CREATE;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.jgit.lib.Constants;
+
+import java.util.Set;
+
+/** Access control management for a user accessing a project's data. */
+public class ProjectControl {
+ public static final int VISIBLE = 1 << 0;
+ public static final int OWNER = 1 << 1;
+
+ public static class Factory {
+ private final ProjectCache projectCache;
+ private final Provider<CurrentUser> user;
+
+ @Inject
+ Factory(final ProjectCache pc, final Provider<CurrentUser> cu) {
+ projectCache = pc;
+ user = cu;
+ }
+
+ public ProjectControl controlFor(final Project.NameKey nameKey)
+ throws NoSuchProjectException {
+ final ProjectState p = projectCache.get(nameKey);
+ if (p == null) {
+ throw new NoSuchProjectException(nameKey);
+ }
+ return p.controlFor(user.get());
+ }
+
+ public ProjectControl validateFor(final Project.NameKey nameKey)
+ throws NoSuchProjectException {
+ return validateFor(nameKey, VISIBLE);
+ }
+
+ public ProjectControl ownerFor(final Project.NameKey nameKey)
+ throws NoSuchProjectException {
+ return validateFor(nameKey, OWNER);
+ }
+
+ public ProjectControl validateFor(final Project.NameKey nameKey,
+ final int need) throws NoSuchProjectException {
+ final ProjectControl c = controlFor(nameKey);
+ if ((need & VISIBLE) == VISIBLE && c.isVisible()) {
+ return c;
+ }
+ if ((need & OWNER) == OWNER && c.isOwner()) {
+ return c;
+ }
+ throw new NoSuchProjectException(nameKey);
+ }
+ }
+
+ private final CurrentUser user;
+ private final ProjectState state;
+
+ ProjectControl(final CurrentUser who, final ProjectState ps) {
+ user = who;
+ state = ps;
+ }
+
+ public ProjectControl forAnonymousUser() {
+ return state.controlForAnonymousUser();
+ }
+
+ public ProjectControl forUser(final CurrentUser who) {
+ return state.controlFor(who);
+ }
+
+ public ChangeControl controlFor(final Change change) {
+ return new ChangeControl(this, change);
+ }
+
+ public CurrentUser getCurrentUser() {
+ return user;
+ }
+
+ public ProjectState getProjectState() {
+ return state;
+ }
+
+ public Project getProject() {
+ return getProjectState().getProject();
+ }
+
+ /** Can this user see this project exists? */
+ public boolean isVisible() {
+ return canPerform(ApprovalCategory.READ, (short) 1);
+ }
+
+ /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
+ public boolean isOwner() {
+ return canPerform(ApprovalCategory.OWN, (short) 1)
+ || getCurrentUser().isAdministrator();
+ }
+
+ /** Can this user create the given ref through this access path? */
+ public boolean canCreateRef(final String refname) {
+ switch (user.getAccessPath()) {
+ case WEB:
+ if (isOwner()) {
+ return true;
+ }
+ if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_CREATE)) {
+ return true;
+ }
+ return false;
+
+ case SSH:
+ if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_CREATE)) {
+ return true;
+ }
+ if (isTag(refname) && canPerform(PUSH_TAG, (short) 1)) {
+ return true;
+ }
+ return false;
+
+ default:
+ return false;
+ }
+ }
+
+ /** Can this user delete the given ref through this access path? */
+ public boolean canDeleteRef(final String refname) {
+ switch (user.getAccessPath()) {
+ case WEB:
+ if (isOwner()) {
+ return true;
+ }
+ if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
+ return true;
+ }
+ return false;
+
+ case SSH:
+ if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
+ return true;
+ }
+ return false;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Can this user perform the action in this project, at the level asked?
+ * <p>
+ * This method checks the project rights against the user's effective groups.
+ * If no right for the given category was granted to any of the user's
+ * effective groups, then the rights from the wildcard project are checked.
+ *
+ * @param actionId unique action id.
+ * @param requireValue minimum value the application needs to perform this
+ * action.
+ * @return true if the action can be performed; false if the user lacks the
+ * necessary permission.
+ */
+ public boolean canPerform(final ApprovalCategory.Id actionId,
+ final short requireValue) {
+ final Set<AccountGroup.Id> groups = user.getEffectiveGroups();
+ int val = Integer.MIN_VALUE;
+ for (final ProjectRight pr : state.getLocalRights()) {
+ if (actionId.equals(pr.getApprovalCategoryId())
+ && groups.contains(pr.getAccountGroupId())) {
+ if (val < 0 && pr.getMaxValue() > 0) {
+ // If one of the user's groups had denied them access, but
+ // this group grants them access, prefer the grant over
+ // the denial. We have to break the tie somehow and we
+ // prefer being "more open" to being "more closed".
+ //
+ val = pr.getMaxValue();
+ } else {
+ // Otherwise we use the largest value we can get.
+ //
+ val = Math.max(pr.getMaxValue(), val);
+ }
+ }
+ }
+ if (val == Integer.MIN_VALUE && actionId.canInheritFromWildProject()) {
+ for (final ProjectRight pr : state.getInheritedRights()) {
+ if (actionId.equals(pr.getApprovalCategoryId())
+ && groups.contains(pr.getAccountGroupId())) {
+ val = Math.max(pr.getMaxValue(), val);
+ }
+ }
+ }
+
+ return val >= requireValue;
+ }
+
+ private static boolean isHead(final String refname) {
+ return refname.startsWith(Constants.R_HEADS);
+ }
+
+ private static boolean isTag(final String refname) {
+ return refname.startsWith(Constants.R_TAGS);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
new file mode 100644
index 0000000000..5e827fbdc3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -0,0 +1,107 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.project;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.server.AnonymousUser;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** Cached information on a project. */
+public class ProjectState {
+ public interface Factory {
+ ProjectState create(Project project, Collection<ProjectRight> localRights,
+ InheritedRights inheritedRights);
+ }
+
+ public interface InheritedRights {
+ Collection<ProjectRight> get();
+ }
+
+ private final AnonymousUser anonymousUser;
+ private final Project.NameKey wildProject;
+
+ private final Project project;
+ private final Collection<ProjectRight> localRights;
+ private final InheritedRights inheritedRights;
+ private final Set<AccountGroup.Id> owners;
+
+ @Inject
+ protected ProjectState(final AnonymousUser anonymousUser,
+ @WildProjectName final Project.NameKey wildProject,
+ @Assisted final Project project,
+ @Assisted final Collection<ProjectRight> rights,
+ @Assisted final InheritedRights inheritedRights) {
+ this.anonymousUser = anonymousUser;
+ this.wildProject = wildProject;
+
+ this.project = project;
+ this.localRights = rights;
+ this.inheritedRights = inheritedRights;
+
+ final HashSet<AccountGroup.Id> groups = new HashSet<AccountGroup.Id>();
+ for (final ProjectRight right : rights) {
+ if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId())
+ && right.getMaxValue() > 0) {
+ groups.add(right.getAccountGroupId());
+ }
+ }
+ owners = Collections.unmodifiableSet(groups);
+ }
+
+ public Project getProject() {
+ return project;
+ }
+
+ /** Get the rights that pertain only to this project. */
+ public Collection<ProjectRight> getLocalRights() {
+ return localRights;
+ }
+
+ /** Get the rights this project inherits from the wild project. */
+ public Collection<ProjectRight> getInheritedRights() {
+ if (isSpecialWildProject()) {
+ return Collections.emptyList();
+ }
+ return inheritedRights.get();
+ }
+
+ /** Is this the special wild project which manages inherited rights? */
+ public boolean isSpecialWildProject() {
+ return project.getNameKey().equals(wildProject);
+ }
+
+ public Set<AccountGroup.Id> getOwners() {
+ return owners;
+ }
+
+ public ProjectControl controlForAnonymousUser() {
+ return controlFor(anonymousUser);
+ }
+
+ public ProjectControl controlFor(final CurrentUser user) {
+ return new ProjectControl(user, this);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/query/AndPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java
index 5e95c18c2b..5e95c18c2b 100644
--- a/src/main/java/com/google/gerrit/server/query/AndPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/AndPredicate.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java
new file mode 100644
index 0000000000..b12b56d8ae
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java
@@ -0,0 +1,79 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.query;
+
+import com.google.gerrit.reviewdb.RevId;
+import com.google.inject.Singleton;
+
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+
+/**
+ * Parses a query string meant to be applied to change objects.
+ * <p>
+ * This class is thread-safe, and may be reused across threads to parse queries.
+ */
+@Singleton
+public class ChangeQueryBuilder extends QueryBuilder {
+ public static final String FIELD_CHANGE = "change";
+ public static final String FIELD_COMMIT = "commit";
+ public static final String FIELD_REVIEWER = "reviewer";
+ public static final String FIELD_OWNER = "owner";
+
+ private static final String CHANGE_RE = "^[1-9][0-9]*$";
+ private static final String COMMIT_RE =
+ "^([0-9a-fA-F]{4," + RevId.LEN + "})$";
+
+ @Operator
+ public Predicate change(final String value) {
+ match(value, CHANGE_RE);
+ return new OperatorPredicate(FIELD_CHANGE, value);
+ }
+
+ @Operator
+ public Predicate commit(final String value) {
+ final AbbreviatedObjectId id = AbbreviatedObjectId.fromString(value);
+ return new ObjectIdPredicate(FIELD_COMMIT, id);
+ }
+
+ @Operator
+ public Predicate owner(final String value) {
+ return new OperatorPredicate(FIELD_OWNER, value);
+ }
+
+ @Operator
+ public Predicate reviewer(final String value) {
+ return new OperatorPredicate(FIELD_REVIEWER, value);
+ }
+
+ @Override
+ protected Predicate defaultField(final String value)
+ throws QueryParseException {
+ if (value.matches(CHANGE_RE)) {
+ return change(value);
+
+ } else if (value.matches(COMMIT_RE)) {
+ return commit(value);
+
+ } else {
+ throw error("Unsupported query:" + value);
+ }
+ }
+
+ private static void match(String val, String re) {
+ if (!val.matches(re)) {
+ throw new IllegalArgumentException("Invalid value :" + val);
+ }
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/query/NotPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
index ddb03a6803..ddb03a6803 100644
--- a/src/main/java/com/google/gerrit/server/query/NotPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/NotPredicate.java
diff --git a/src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java
index bd9eeeaad7..bd9eeeaad7 100644
--- a/src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/ObjectIdPredicate.java
diff --git a/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
index fbd6af1225..fbd6af1225 100644
--- a/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OperatorPredicate.java
diff --git a/src/main/java/com/google/gerrit/server/query/OrPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
index a8b8d2b8d0..a8b8d2b8d0 100644
--- a/src/main/java/com/google/gerrit/server/query/OrPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/OrPredicate.java
diff --git a/src/main/java/com/google/gerrit/server/query/Predicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
index 70da79db8a..70da79db8a 100644
--- a/src/main/java/com/google/gerrit/server/query/Predicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/Predicate.java
diff --git a/src/main/java/com/google/gerrit/server/query/QueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
index da967f45ce..da967f45ce 100644
--- a/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryBuilder.java
diff --git a/src/main/java/com/google/gerrit/server/query/QueryParseException.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryParseException.java
index 346e99de3a..346e99de3a 100644
--- a/src/main/java/com/google/gerrit/server/query/QueryParseException.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/QueryParseException.java
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshInfo.java
new file mode 100644
index 0000000000..0081cb4431
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshInfo.java
@@ -0,0 +1,23 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.ssh;
+
+import com.jcraft.jsch.HostKey;
+
+import java.util.List;
+
+public interface SshInfo {
+ List<HostKey> getHostKeys();
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java
new file mode 100644
index 0000000000..b56405afb8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.ssh;
+
+import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.reviewdb.AccountSshKey;
+
+/** Permits controlling the contents of the SSH key cache area. */
+public interface SshKeyCache {
+ public void evict(String username);
+
+ public AccountSshKey create(AccountSshKey.Id id, String encoded)
+ throws InvalidSshKeyException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
new file mode 100644
index 0000000000..38a9fd4730
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.workflow;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.server.CurrentUser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Function to control {@link PatchSetApproval}s in an {@link ApprovalCategory}. */
+public abstract class CategoryFunction {
+ private static Map<String, CategoryFunction> all =
+ new HashMap<String, CategoryFunction>();
+ static {
+ all.put(SubmitFunction.NAME, new SubmitFunction());
+ all.put(MaxWithBlock.NAME, new MaxWithBlock());
+ all.put(NoOpFunction.NAME, new NoOpFunction());
+ }
+
+ /**
+ * Locate a function by category.
+ *
+ * @param category the category the function is for.
+ * @return the function implementation; {@link NoOpFunction} if the function
+ * is not known to Gerrit and thus cannot be executed.
+ */
+ public static CategoryFunction forCategory(final ApprovalCategory category) {
+ final CategoryFunction r = forName(category.getFunctionName());
+ return r != null ? r : new NoOpFunction();
+ }
+
+ /**
+ * Locate a function by name.
+ *
+ * @param functionName the function's unique name.
+ * @return the function implementation; null if the function is not known to
+ * Gerrit and thus cannot be executed.
+ */
+ public static CategoryFunction forName(final String functionName) {
+ return all.get(functionName);
+ }
+
+ /**
+ * Normalize ChangeApprovals and set the valid flag for this category.
+ * <p>
+ * Implementors should invoke:
+ *
+ * <pre>
+ * state.valid(at, true);
+ * </pre>
+ * <p>
+ * If the set of approvals from <code>state.getApprovals(at)</code> covers the
+ * requirements for the function, indicating the category has been completed.
+ * <p>
+ * An example implementation which requires at least one positive and no
+ * negatives might be:
+ *
+ * <pre>
+ * boolean neg = false, pos = false;
+ * for (final ChangeApproval ca : state.getApprovals(at)) {
+ * state.normalize(ca);
+ * neg |= ca.getValue() &lt; 0;
+ * pos |= ca.getValue() &gt; 0;
+ * }
+ * state.valid(at, !neg &amp;&amp; pos);
+ * </pre>
+ *
+ * @param at the cached category description to process.
+ * @param state state to read approvals and project rights from, and to update
+ * the valid status into.
+ */
+ public abstract void run(ApprovalType at, FunctionState state);
+
+ public boolean isValid(final CurrentUser user, final ApprovalType at,
+ final FunctionState state) {
+ for (final ProjectRight pr : state.getAllRights(at)) {
+ if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
+ && (pr.getMinValue() < 0 || pr.getMaxValue() > 0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
new file mode 100644
index 0000000000..69f571d47c
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
@@ -0,0 +1,261 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.workflow;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ApprovalCategory.Id;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** State passed through to a {@link CategoryFunction}. */
+public class FunctionState {
+ public interface Factory {
+ FunctionState create(Change c, PatchSet.Id psId,
+ Collection<PatchSetApproval> all);
+ }
+
+ private final ApprovalTypes approvalTypes;
+ private final IdentifiedUser.GenericFactory userFactory;
+
+ private final Map<ApprovalCategory.Id, Collection<PatchSetApproval>> approvals =
+ new HashMap<ApprovalCategory.Id, Collection<PatchSetApproval>>();
+ private final Map<ApprovalCategory.Id, Boolean> valid =
+ new HashMap<ApprovalCategory.Id, Boolean>();
+ private final Change change;
+ private final ProjectState project;
+ private final Map<ApprovalCategory.Id, Collection<ProjectRight>> allRights =
+ new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>();
+ private Map<ApprovalCategory.Id, Collection<ProjectRight>> projectRights;
+ private Map<ApprovalCategory.Id, Collection<ProjectRight>> inheritedRights;
+ private Set<PatchSetApproval> modified;
+
+ @Inject
+ FunctionState(final ApprovalTypes approvalTypes,
+ final ProjectCache projectCache,
+ final IdentifiedUser.GenericFactory userFactory, final GroupCache egc,
+ @Assisted final Change c, @Assisted final PatchSet.Id psId,
+ @Assisted final Collection<PatchSetApproval> all) {
+ this.approvalTypes = approvalTypes;
+ this.userFactory = userFactory;
+
+ change = c;
+ project = projectCache.get(change.getProject());
+
+ for (final PatchSetApproval ca : all) {
+ if (psId.equals(ca.getPatchSetId())) {
+ Collection<PatchSetApproval> l = approvals.get(ca.getCategoryId());
+ if (l == null) {
+ l = new ArrayList<PatchSetApproval>();
+ approvals.put(ca.getCategoryId(), l);
+ }
+ l.add(ca);
+ }
+ }
+ }
+
+ List<ApprovalType> getApprovalTypes() {
+ return approvalTypes.getApprovalTypes();
+ }
+
+ public Change getChange() {
+ return change;
+ }
+
+ public Project getProject() {
+ return project.getProject();
+ }
+
+ public void valid(final ApprovalType at, final boolean v) {
+ valid.put(id(at), v);
+ }
+
+ public boolean isValid(final ApprovalType at) {
+ return isValid(id(at));
+ }
+
+ public boolean isValid(final ApprovalCategory.Id id) {
+ final Boolean b = valid.get(id);
+ return b != null && b;
+ }
+
+ public Collection<PatchSetApproval> getApprovals(final ApprovalType at) {
+ return getApprovals(id(at));
+ }
+
+ public Collection<PatchSetApproval> getApprovals(final ApprovalCategory.Id id) {
+ final Collection<PatchSetApproval> l = approvals.get(id);
+ return l != null ? l : Collections.<PatchSetApproval> emptySet();
+ }
+
+ public void dirty(final PatchSetApproval ap) {
+ if (modified == null) {
+ modified = new HashSet<PatchSetApproval>();
+ }
+ modified.add(ap);
+ }
+
+ public Collection<PatchSetApproval> getDirtyChangeApprovals() {
+ if (modified != null) {
+ return modified;
+ }
+ return Collections.emptySet();
+ }
+
+ public Collection<ProjectRight> getProjectRights(final ApprovalType at) {
+ return getProjectRights(id(at));
+ }
+
+ public Collection<ProjectRight> getProjectRights(final ApprovalCategory.Id id) {
+ if (projectRights == null) {
+ projectRights = index(project.getLocalRights());
+ }
+ final Collection<ProjectRight> l = projectRights.get(id);
+ return l != null ? l : Collections.<ProjectRight> emptySet();
+ }
+
+ public Collection<ProjectRight> getWildcardRights(final ApprovalType at) {
+ return getWildcardRights(id(at));
+ }
+
+ public Collection<ProjectRight> getWildcardRights(final ApprovalCategory.Id id) {
+ if (inheritedRights == null) {
+ inheritedRights = index(project.getInheritedRights());
+ }
+ final Collection<ProjectRight> l = inheritedRights.get(id);
+ return l != null ? l : Collections.<ProjectRight> emptySet();
+ }
+
+ public Collection<ProjectRight> getAllRights(final ApprovalType at) {
+ return getAllRights(id(at));
+ }
+
+ public Collection<ProjectRight> getAllRights(final ApprovalCategory.Id id) {
+ Collection<ProjectRight> l = allRights.get(id);
+ if (l == null) {
+ l = new ArrayList<ProjectRight>();
+ l.addAll(getProjectRights(id));
+ l.addAll(getWildcardRights(id));
+ l = Collections.unmodifiableCollection(l);
+ allRights.put(id, l);
+ }
+ return l;
+ }
+
+ private static Map<Id, Collection<ProjectRight>> index(
+ final Collection<ProjectRight> rights) {
+ final HashMap<ApprovalCategory.Id, Collection<ProjectRight>> r;
+
+ r = new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>();
+ for (final ProjectRight pr : rights) {
+ Collection<ProjectRight> l = r.get(pr.getApprovalCategoryId());
+ if (l == null) {
+ l = new ArrayList<ProjectRight>();
+ r.put(pr.getApprovalCategoryId(), l);
+ }
+ l.add(pr);
+ }
+ return r;
+ }
+
+ /**
+ * Normalize the approval record down to the range permitted by the type, in
+ * case the type was modified since the approval was originally granted.
+ * <p>
+ * If the record's value was modified, its automatically marked as dirty.
+ */
+ public void applyTypeFloor(final ApprovalType at, final PatchSetApproval a) {
+ final ApprovalCategoryValue atMin = at.getMin();
+
+ if (atMin != null && a.getValue() < atMin.getValue()) {
+ a.setValue(atMin.getValue());
+ dirty(a);
+ }
+
+ final ApprovalCategoryValue atMax = at.getMax();
+ if (atMax != null && a.getValue() > atMax.getValue()) {
+ a.setValue(atMax.getValue());
+ dirty(a);
+ }
+ }
+
+ /**
+ * Normalize the approval record to be inside the maximum range permitted by
+ * the ProjectRights granted to groups the account is a member of.
+ * <p>
+ * If multiple ProjectRights are matched (assigned to different groups the
+ * account is a member of) the lowest minValue and the highest maxValue of the
+ * union of them is used.
+ * <p>
+ * If the record's value was modified, its automatically marked as dirty.
+ */
+ public void applyRightFloor(final PatchSetApproval a) {
+ final IdentifiedUser user = userFactory.create(a.getAccountId());
+
+ // Find the maximal range actually granted to the user.
+ //
+ short minAllowed = 0, maxAllowed = 0;
+ for (final ProjectRight r : getAllRights(a.getCategoryId())) {
+ final AccountGroup.Id grp = r.getAccountGroupId();
+ if (user.getEffectiveGroups().contains(grp)) {
+ minAllowed = (short) Math.min(minAllowed, r.getMinValue());
+ maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
+ }
+ }
+
+ // Normalize the value into that range, returning true if we changed
+ // the value.
+ //
+ if (a.getValue() < minAllowed) {
+ a.setValue(minAllowed);
+ dirty(a);
+
+ } else if (a.getValue() > maxAllowed) {
+ a.setValue(maxAllowed);
+ dirty(a);
+ }
+ }
+
+ /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */
+ public void normalize(final ApprovalType at, final PatchSetApproval ca) {
+ applyTypeFloor(at, ca);
+ applyRightFloor(ca);
+ }
+
+ private static Id id(final ApprovalType at) {
+ return at.getCategory().getId();
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java
new file mode 100644
index 0000000000..7bf7e9bcbe
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java
@@ -0,0 +1,63 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.workflow;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+
+/**
+ * Computes an {@link ApprovalCategory} by looking at maximum values.
+ * <p>
+ * In order to be considered "approved" this function requires that:
+ * <ul>
+ * <li>The maximum negative value is never used;</li>
+ * <li>The maximum positive value is used at least once;</li>
+ * <li>The user approving the maximum positive has been granted that.</li>
+ * </ul>
+ * <p>
+ * This function is primarily useful for review fields, with values such as:
+ * <ul>
+ * <li>+2: Approved change.</li>
+ * <li>+1: Looks ok, but get another approval from someone with more depth.</li>
+ * <li>-1: Soft reject, it isn't a great change but its OK if approved.</li>
+ * <li>-2: Rejected, must not be submitted.
+ * </ul>
+ * <p>
+ * Note that projects using this function would typically want to assign out the
+ * middle range (-1 .. +1) to almost everyone, so people can indicate how they
+ * feel about a change, but the extremes of -2 and +2 should be reserved for the
+ * project's long-term maintainers, those who are most familiar with its code.
+ */
+public class MaxWithBlock extends CategoryFunction {
+ public static String NAME = "MaxWithBlock";
+
+ @Override
+ public void run(final ApprovalType at, final FunctionState state) {
+ boolean rejected = false;
+ boolean passed = false;
+ for (final PatchSetApproval a : state.getApprovals(at)) {
+ state.normalize(at, a);
+
+ rejected |= at.isMaxNegative(a);
+ passed |= at.isMaxPositive(a);
+ }
+
+ // The type must not have had its max negative (a forceful reject)
+ // and must have at least one max positive (a full accept).
+ //
+ state.valid(at, !rejected && passed);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java
new file mode 100644
index 0000000000..6d2c26cd10
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.workflow;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.server.CurrentUser;
+
+/** A function that does nothing. */
+public class NoOpFunction extends CategoryFunction {
+ public static String NAME = "NoOp";
+
+ @Override
+ public void run(final ApprovalType at, final FunctionState state) {
+ }
+
+ @Override
+ public boolean isValid(final CurrentUser user, final ApprovalType at,
+ final FunctionState state) {
+ return false;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
new file mode 100644
index 0000000000..b9805796f2
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
@@ -0,0 +1,66 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.workflow;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.server.CurrentUser;
+
+/**
+ * Computes if the submit function can be used.
+ * <p>
+ * In order to be considered "approved" this function requires that all approval
+ * categories with a position >= 0 (that is any whose
+ * {@link ApprovalCategory#isAction()} method returns false) is valid and that
+ * the change state be {@link Change.Status#NEW}.
+ * <p>
+ * This is mostly useful for actions, like {@link ApprovalCategory#SUBMIT}.
+ */
+public class SubmitFunction extends CategoryFunction {
+ public static String NAME = "Submit";
+
+ @Override
+ public void run(final ApprovalType at, final FunctionState state) {
+ state.valid(at, valid(at, state));
+ }
+
+ @Override
+ public boolean isValid(final CurrentUser user, final ApprovalType at,
+ final FunctionState state) {
+ if (valid(at, state)) {
+ for (final ProjectRight pr : state.getAllRights(at)) {
+ if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
+ && pr.getMaxValue() > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean valid(final ApprovalType at, final FunctionState state) {
+ if (state.getChange().getStatus() != Change.Status.NEW) {
+ return false;
+ }
+ for (final ApprovalType t : state.getApprovalTypes()) {
+ if (!state.isValid(t)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/test/java/com/google/gerrit/server/ParamertizedStringTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ParamertizedStringTest.java
index c64aebd691..c64aebd691 100644
--- a/src/test/java/com/google/gerrit/server/ParamertizedStringTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/ParamertizedStringTest.java
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java
new file mode 100644
index 0000000000..6a1777c19f
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java
@@ -0,0 +1,362 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.config;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.SchemaVersion;
+import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.server.workflow.NoOpFunction;
+import com.google.gerrit.server.workflow.SubmitFunction;
+import com.google.gerrit.testutil.TestDatabase;
+import com.google.gwtorm.client.OrmException;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.sql.SQLException;
+import java.util.HashSet;
+
+public class SystemConfigProviderTest extends TestCase {
+ private ApprovalCategory.Id codeReview = new ApprovalCategory.Id("CRVW");
+ private TestDatabase db;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ db = new TestDatabase();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ TestDatabase.drop(db);
+ super.tearDown();
+ }
+
+ public void testGetCauses_CreateSchema() throws OrmException {
+ // Initially the schema should be empty.
+ //
+ try {
+ getSchemaVersion();
+ fail("Brand new test database has schema_version table");
+ } catch (OrmException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SQLException);
+
+ final String msg = cause.getMessage();
+ assertEquals("Table SCHEMA_VERSION not found", msg.split(";")[0]);
+ }
+
+ // Create the schema using the current schema version.
+ //
+ final SystemConfig config = getSystemConfig();
+ final SchemaVersion version = getSchemaVersion();
+ assertNotNull(version);
+ assertEquals(ReviewDb.VERSION, version.versionNbr);
+
+ assertNotNull(config);
+ assertNotNull(config.adminGroupId);
+ assertNotNull(config.anonymousGroupId);
+ assertNotNull(config.registeredGroupId);
+
+ // By default sitePath is set to the current working directory.
+ //
+ File sitePath = new File(".").getAbsoluteFile();
+ if (sitePath.getName().equals(".")) {
+ sitePath = sitePath.getParentFile();
+ }
+ assertEquals(sitePath.getAbsolutePath(), config.sitePath);
+
+ // This is randomly generated and should be at least 20 bytes long.
+ //
+ assertNotNull(config.registerEmailPrivateKey);
+ assertTrue(20 < config.registerEmailPrivateKey.length());
+ }
+
+ public void testSubsequentGetReads() {
+ final SystemConfig exp = getSystemConfig();
+ final SystemConfig act = getSystemConfig();
+
+ assertNotSame(exp, act);
+ assertEquals(exp.adminGroupId, act.adminGroupId);
+ assertEquals(exp.anonymousGroupId, act.anonymousGroupId);
+ assertEquals(exp.registeredGroupId, act.registeredGroupId);
+ assertEquals(exp.sitePath, act.sitePath);
+ assertEquals(exp.registerEmailPrivateKey, act.registerEmailPrivateKey);
+ }
+
+ public void testCreateSchema_Group_Administrators() throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ final ReviewDb c = db.open();
+ try {
+ final AccountGroup admin = c.accountGroups().get(config.adminGroupId);
+ assertNotNull(admin);
+ assertEquals(config.adminGroupId, admin.getId());
+ assertEquals("Administrators", admin.getName());
+ assertSame(AccountGroup.Type.INTERNAL, admin.getType());
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testCreateSchema_Group_AnonymousUsers() throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ final ReviewDb c = db.open();
+ try {
+ final AccountGroup anon = c.accountGroups().get(config.anonymousGroupId);
+ assertNotNull(anon);
+ assertEquals(config.anonymousGroupId, anon.getId());
+ assertEquals("Anonymous Users", anon.getName());
+ assertSame(AccountGroup.Type.SYSTEM, anon.getType());
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testCreateSchema_Group_RegisteredUsers() throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ final ReviewDb c = db.open();
+ try {
+ final AccountGroup reg = c.accountGroups().get(config.registeredGroupId);
+ assertNotNull(reg);
+ assertEquals(config.registeredGroupId, reg.getId());
+ assertEquals("Registered Users", reg.getName());
+ assertSame(AccountGroup.Type.SYSTEM, reg.getType());
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testCreateSchema_WildCardProject() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final Project all;
+
+ all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
+ assertNotNull(all);
+ assertEquals("-- All Projects --", all.getName());
+ assertEquals(new Project.Id(0), all.getId());
+ assertFalse(all.isUseContributorAgreements());
+ assertFalse(all.isUseSignedOffBy());
+ } finally {
+ c.close();
+ }
+ }
+
+ public void testCreateSchema_ApprovalCategory_CodeReview()
+ throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(codeReview);
+ assertNotNull(cat);
+ assertEquals(codeReview, cat.getId());
+ assertEquals("Code Review", cat.getName());
+ assertEquals("R", cat.getAbbreviatedName());
+ assertEquals("MaxWithBlock", cat.getFunctionName());
+ assertTrue(cat.isCopyMinScore());
+ assertFalse(cat.isAction());
+ assertTrue(0 <= cat.getPosition());
+ } finally {
+ c.close();
+ }
+ assertValueRange(codeReview, -2, -1, 0, 1, 2);
+ }
+
+ public void testCreateSchema_ApprovalCategory_Read() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(ApprovalCategory.READ);
+ assertNotNull(cat);
+ assertEquals(ApprovalCategory.READ, cat.getId());
+ assertEquals("Read Access", cat.getName());
+ assertNull(cat.getAbbreviatedName());
+ assertEquals(NoOpFunction.NAME, cat.getFunctionName());
+ assertTrue(cat.isAction());
+ } finally {
+ c.close();
+ }
+ assertValueRange(ApprovalCategory.READ, -1, 1, 2);
+ }
+
+ public void testCreateSchema_ApprovalCategory_Submit() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(ApprovalCategory.SUBMIT);
+ assertNotNull(cat);
+ assertEquals(ApprovalCategory.SUBMIT, cat.getId());
+ assertEquals("Submit", cat.getName());
+ assertNull(cat.getAbbreviatedName());
+ assertEquals(SubmitFunction.NAME, cat.getFunctionName());
+ assertTrue(cat.isAction());
+ } finally {
+ c.close();
+ }
+ assertValueRange(ApprovalCategory.SUBMIT, 1);
+ }
+
+ public void testCreateSchema_ApprovalCategory_PushTag() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(ApprovalCategory.PUSH_TAG);
+ assertNotNull(cat);
+ assertEquals(ApprovalCategory.PUSH_TAG, cat.getId());
+ assertEquals("Push Annotated Tag", cat.getName());
+ assertNull(cat.getAbbreviatedName());
+ assertEquals(NoOpFunction.NAME, cat.getFunctionName());
+ assertTrue(cat.isAction());
+ } finally {
+ c.close();
+ }
+ assertValueRange(ApprovalCategory.PUSH_TAG, //
+ ApprovalCategory.PUSH_TAG_SIGNED, //
+ ApprovalCategory.PUSH_TAG_ANNOTATED, //
+ ApprovalCategory.PUSH_TAG_ANY);
+ }
+
+ public void testCreateSchema_ApprovalCategory_PushHead() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(ApprovalCategory.PUSH_HEAD);
+ assertNotNull(cat);
+ assertEquals(ApprovalCategory.PUSH_HEAD, cat.getId());
+ assertEquals("Push Branch", cat.getName());
+ assertNull(cat.getAbbreviatedName());
+ assertEquals(NoOpFunction.NAME, cat.getFunctionName());
+ assertTrue(cat.isAction());
+ } finally {
+ c.close();
+ }
+ assertValueRange(ApprovalCategory.PUSH_HEAD, //
+ ApprovalCategory.PUSH_HEAD_UPDATE, //
+ ApprovalCategory.PUSH_HEAD_CREATE, //
+ ApprovalCategory.PUSH_HEAD_REPLACE);
+ }
+
+ public void testCreateSchema_ApprovalCategory_Owner() throws OrmException {
+ final ReviewDb c = db.create().open();
+ try {
+ final ApprovalCategory cat;
+
+ cat = c.approvalCategories().get(ApprovalCategory.OWN);
+ assertNotNull(cat);
+ assertEquals(ApprovalCategory.OWN, cat.getId());
+ assertEquals("Owner", cat.getName());
+ assertNull(cat.getAbbreviatedName());
+ assertEquals(NoOpFunction.NAME, cat.getFunctionName());
+ assertTrue(cat.isAction());
+ } finally {
+ c.close();
+ }
+ assertValueRange(ApprovalCategory.OWN, 1);
+ }
+
+ private void assertValueRange(ApprovalCategory.Id cat, int... range)
+ throws OrmException {
+ final HashSet<ApprovalCategoryValue.Id> act =
+ new HashSet<ApprovalCategoryValue.Id>();
+ final ReviewDb c = db.open();
+ try {
+ for (ApprovalCategoryValue v : c.approvalCategoryValues().byCategory(cat)) {
+ assertNotNull(v.getId());
+ assertNotNull(v.getName());
+ assertEquals(cat, v.getCategoryId());
+ assertFalse(v.getName().isEmpty());
+
+ act.add(v.getId());
+ }
+ } finally {
+ c.close();
+ }
+
+ for (int value : range) {
+ final ApprovalCategoryValue.Id exp =
+ new ApprovalCategoryValue.Id(cat, (short) value);
+ if (!act.remove(exp)) {
+ fail("Category " + cat + " lacks value " + value);
+ }
+ }
+ if (!act.isEmpty()) {
+ fail("Category " + cat + " has additional values: " + act);
+ }
+ }
+
+ public void testCreateSchema_DefaultAccess_AnonymousUsers()
+ throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ assertDefaultRight(config.anonymousGroupId, ApprovalCategory.READ, 1, 1);
+ }
+
+ public void testCreateSchema_DefaultAccess_RegisteredUsers()
+ throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ assertDefaultRight(config.registeredGroupId, ApprovalCategory.READ, 1, 2);
+ assertDefaultRight(config.registeredGroupId, codeReview, -1, 1);
+ }
+
+ public void testCreateSchema_DefaultAccess_Administrators()
+ throws OrmException {
+ final SystemConfig config = getSystemConfig();
+ assertDefaultRight(config.adminGroupId, ApprovalCategory.READ, 1, 1);
+ }
+
+ private void assertDefaultRight(final AccountGroup.Id group,
+ final ApprovalCategory.Id category, int min, int max) throws OrmException {
+ final ReviewDb c = db.open();
+ try {
+ final Project all;
+ final ProjectRight right;
+
+ all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
+ right = c.projectRights().get( //
+ new ProjectRight.Key(all.getNameKey(), category, group));
+
+ assertNotNull(right);
+ assertEquals(all.getNameKey(), right.getProjectNameKey());
+ assertEquals(group, right.getAccountGroupId());
+ assertEquals(category, right.getApprovalCategoryId());
+ assertEquals(min, right.getMinValue());
+ assertEquals(max, right.getMaxValue());
+ } finally {
+ c.close();
+ }
+ }
+
+ private SystemConfig getSystemConfig() {
+ return new SystemConfigProvider(db).get();
+ }
+
+ private SchemaVersion getSchemaVersion() throws OrmException {
+ final ReviewDb c = db.open();
+ try {
+ return c.schemaVersion().get(new SchemaVersion.Key());
+ } finally {
+ c.close();
+ }
+ }
+}
diff --git a/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
index 38e6a0ff3f..38e6a0ff3f 100644
--- a/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/ioutil/BasicSerializationTest.java
diff --git a/src/test/java/com/google/gerrit/server/mail/AddressTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java
index bfa5f4f12d..bfa5f4f12d 100644
--- a/src/test/java/com/google/gerrit/server/mail/AddressTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/AddressTest.java
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
new file mode 100644
index 0000000000..f819dacdca
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -0,0 +1,285 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.mail;
+
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+
+import java.util.Collections;
+
+public class FromAddressGeneratorProviderTest extends TestCase {
+ private Config config;
+ private PersonIdent ident;
+ private AccountCache accountCache;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ config = new Config();
+ ident = new PersonIdent("NAME", "e@email", 0, 0);
+ accountCache = createStrictMock(AccountCache.class);
+ }
+
+ private FromAddressGenerator create() {
+ return new FromAddressGeneratorProvider(config, ident, accountCache).get();
+ }
+
+ private void setFrom(final String newFrom) {
+ config.setString("sendemail", null, "from", newFrom);
+ }
+
+ public void testDefaultIsMIXED() {
+ assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
+ }
+
+ public void testSelectUSER() {
+ setFrom("USER");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
+
+ setFrom("user");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
+
+ setFrom("uSeR");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
+ }
+
+ public void testUSER_FullyConfiguredUser() {
+ setFrom("USER");
+
+ final String name = "A U. Thor";
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(name, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(name, r.name);
+ assertEquals(email, r.email);
+ verify(accountCache);
+ }
+
+ public void testUSER_NoFullNameUser() {
+ setFrom("USER");
+
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(null, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(null, r.name);
+ assertEquals(email, r.email);
+ verify(accountCache);
+ }
+
+ public void testUSER_NoPreferredEmailUser() {
+ setFrom("USER");
+
+ final Account.Id user = user("A U. Thor", null);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testUSER_NullUser() {
+ setFrom("USER");
+ replay(accountCache);
+ final Address r = create().from(null);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testSelectSERVER() {
+ setFrom("SERVER");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
+
+ setFrom("server");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
+
+ setFrom("sErVeR");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
+ }
+
+ public void testSERVER_FullyConfiguredUser() {
+ setFrom("SERVER");
+
+ final String name = "A U. Thor";
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = userNoLookup(name, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testSERVER_NullUser() {
+ setFrom("SERVER");
+ replay(accountCache);
+ final Address r = create().from(null);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testSelectMIXED() {
+ setFrom("MIXED");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
+
+ setFrom("mixed");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
+
+ setFrom("mIxEd");
+ assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
+ }
+
+ public void testMIXED_FullyConfiguredUser() {
+ setFrom("MIXED");
+
+ final String name = "A U. Thor";
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(name, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(name + " (Code Review)", r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testMIXED_NoFullNameUser() {
+ setFrom("MIXED");
+
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(null, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals("Anonymous Coward (Code Review)", r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testMIXED_NoPreferredEmailUser() {
+ setFrom("MIXED");
+
+ final String name = "A U. Thor";
+ final Account.Id user = user(name, null);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals(name + " (Code Review)", r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testMIXED_NullUser() {
+ setFrom("MIXED");
+ replay(accountCache);
+ final Address r = create().from(null);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals(ident.getEmailAddress(), r.email);
+ verify(accountCache);
+ }
+
+ public void testCUSTOM_FullyConfiguredUser() {
+ setFrom("A ${user} B <my.server@email.address>");
+
+ final String name = "A U. Thor";
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(name, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals("A " + name + " B", r.name);
+ assertEquals("my.server@email.address", r.email);
+ verify(accountCache);
+ }
+
+ public void testCUSTOM_NoFullNameUser() {
+ setFrom("A ${user} B <my.server@email.address>");
+
+ final String email = "a.u.thor@test.example.com";
+ final Account.Id user = user(null, email);
+
+ replay(accountCache);
+ final Address r = create().from(user);
+ assertNotNull(r);
+ assertEquals("A Anonymous Coward B", r.name);
+ assertEquals("my.server@email.address", r.email);
+ verify(accountCache);
+ }
+
+ public void testCUSTOM_NullUser() {
+ setFrom("A ${user} B <my.server@email.address>");
+
+ replay(accountCache);
+ final Address r = create().from(null);
+ assertNotNull(r);
+ assertEquals(ident.getName(), r.name);
+ assertEquals("my.server@email.address", r.email);
+ verify(accountCache);
+ }
+
+ private Account.Id user(final String name, final String email) {
+ final AccountState s = makeUser(name, email);
+ expect(accountCache.get(eq(s.getAccount().getId()))).andReturn(s);
+ return s.getAccount().getId();
+ }
+
+ private Account.Id userNoLookup(final String name, final String email) {
+ final AccountState s = makeUser(name, email);
+ return s.getAccount().getId();
+ }
+
+ private AccountState makeUser(final String name, final String email) {
+ final Account.Id userId = new Account.Id(42);
+ final Account account = new Account(userId);
+ account.setFullName(name);
+ account.setPreferredEmail(email);
+ final AccountState s =
+ new AccountState(account, Collections.<AccountGroup.Id> emptySet(),
+ Collections.<AccountExternalId> emptySet());
+ return s;
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
new file mode 100644
index 0000000000..24f6f632b3
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.server.patch;
+
+import com.google.gerrit.reviewdb.Patch;
+
+import junit.framework.TestCase;
+
+public class PatchListEntryTest extends TestCase {
+ public void testEmpty1() {
+ final String name = "empty-file";
+ final PatchListEntry e = PatchListEntry.empty(name);
+ assertNull(e.getOldName());
+ assertEquals(name, e.getNewName());
+ assertSame(Patch.PatchType.UNIFIED, e.getPatchType());
+ assertSame(Patch.ChangeType.MODIFIED, e.getChangeType());
+ assertTrue(e.getEdits().isEmpty());
+ }
+}
diff --git a/src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java
index 4dc8ba5ad8..4dc8ba5ad8 100644
--- a/src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/ChangeQueryBuilderTest.java
diff --git a/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
index 63cf166951..63cf166951 100644
--- a/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/FieldPredicateTest.java
diff --git a/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
index 23ba24ed81..23ba24ed81 100644
--- a/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/NotPredicateTest.java
diff --git a/src/test/java/com/google/gerrit/server/query/QueryParserTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
index 9534d2b4d5..9534d2b4d5 100644
--- a/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/QueryParserTest.java
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java
new file mode 100644
index 0000000000..7dc504fc5f
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestDatabase.java
@@ -0,0 +1,105 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.testutil;
+
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.config.SystemConfigProvider;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.gwtorm.jdbc.Database;
+import com.google.gwtorm.jdbc.SimpleDataSource;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+
+/**
+ * An in-memory test instance of {@link ReviewDb} database.
+ * <p>
+ * Test classes should create one instance of this class for each unique test
+ * database they want to use. When the tests needing this instance are complete,
+ * ensure that {@link #drop(TestDatabase)} is called to free the resources so
+ * the JVM running the unit tests doesn't run out of heap space.
+ */
+public class TestDatabase implements SchemaFactory<ReviewDb> {
+ private static int dbCnt;
+
+ private static synchronized DataSource newDataSource() throws SQLException {
+ final Properties p = new Properties();
+ p.setProperty("driver", org.h2.Driver.class.getName());
+ p.setProperty("url", "jdbc:h2:mem:" + "Test_" + (++dbCnt));
+ final DataSource dataSource = new SimpleDataSource(p);
+ return dataSource;
+ }
+
+ /** Drop the database from memory; does nothing if the instance was null. */
+ public static void drop(final TestDatabase db) {
+ if (db != null) {
+ db.drop();
+ }
+ }
+
+ private Connection openHandle;
+ private Database<ReviewDb> database;
+
+ public TestDatabase() throws OrmException {
+ try {
+ final DataSource dataSource = newDataSource();
+
+ // Open one connection. This will peg the database into memory
+ // until someone calls drop on us, allowing subsequent connections
+ // opened against the same URL to go to the same set of tables.
+ //
+ openHandle = dataSource.getConnection();
+
+ // Build the access layer around the connection factory.
+ //
+ database = new Database<ReviewDb>(dataSource, ReviewDb.class);
+ } catch (SQLException e) {
+ throw new OrmException(e);
+ }
+ }
+
+ public Database<ReviewDb> getDatabase() {
+ return database;
+ }
+
+ @Override
+ public ReviewDb open() throws OrmException {
+ return getDatabase().open();
+ }
+
+ /** Ensure the database schema has been created and initialized. */
+ public TestDatabase create() {
+ new SystemConfigProvider(this).get();
+ return this;
+ }
+
+ /** Drop this database from memory so it no longer exists. */
+ public void drop() {
+ if (openHandle != null) {
+ try {
+ openHandle.close();
+ } catch (SQLException e) {
+ System.err.println("WARNING: Cannot close database connection");
+ e.printStackTrace(System.err);
+ }
+ openHandle = null;
+ database = null;
+ }
+ }
+}
diff --git a/gerrit-sshd/.gitignore b/gerrit-sshd/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-sshd/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-sshd/.settings/org.eclipse.core.resources.prefs b/gerrit-sshd/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-sshd/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-sshd/.settings/org.eclipse.core.runtime.prefs b/gerrit-sshd/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-sshd/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-sshd/.settings/org.eclipse.jdt.core.prefs b/gerrit-sshd/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-sshd/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-sshd/.settings/org.eclipse.jdt.ui.prefs b/gerrit-sshd/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-sshd/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-sshd/pom.xml b/gerrit-sshd/pom.xml
new file mode 100644
index 0000000000..d24e097166
--- /dev/null
+++ b/gerrit-sshd/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-sshd</artifactId>
+ <name>Gerrit Code Review - SSHd</name>
+
+ <description>
+ Java SSH daemon with Gerrit commands and Git support
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-cli</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-server</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ </plugins>
+ </build>
+</project>
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java
new file mode 100644
index 0000000000..adaf6466fa
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AdminCommand.java
@@ -0,0 +1,32 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation tagged on a concrete Command that requires administrator access.
+ * <p>
+ * Currently this annotation is only enforced by DispatchCommand after it has
+ * created the command object, but before it populates it or starts execution.
+ */
+@Target( {ElementType.TYPE})
+@Retention(RUNTIME)
+public @interface AdminCommand {
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
new file mode 100644
index 0000000000..060f8f82d0
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -0,0 +1,454 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.RequestCleanup;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.sshd.SshScopes.Context;
+import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.inject.Inject;
+
+import org.apache.sshd.common.SshException;
+import org.apache.sshd.server.CommandFactory.Command;
+import org.apache.sshd.server.CommandFactory.ExitCallback;
+import org.apache.sshd.server.session.ServerSession;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.Option;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class BaseCommand implements Command {
+ private static final Logger log = LoggerFactory.getLogger(BaseCommand.class);
+ public static final String ENC = "UTF-8";
+
+ @Option(name = "--help", usage = "display this help text", aliases = {"-h"})
+ private boolean help;
+
+ protected InputStream in;
+ protected OutputStream out;
+ protected OutputStream err;
+
+ private ExitCallback exit;
+
+ @Inject
+ private CmdLineParser.Factory cmdLineParserFactory;
+
+ @Inject
+ private RequestCleanup cleanup;
+
+ /** Text of the command line which lead up to invoking this instance. */
+ protected String commandPrefix = "";
+
+ /** Unparsed rest of the command line. */
+ protected String commandLine = "";
+
+ public void setInputStream(final InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(final OutputStream out) {
+ this.out = out;
+ }
+
+ public void setErrorStream(final OutputStream err) {
+ this.err = err;
+ }
+
+ public void setExitCallback(final ExitCallback callback) {
+ this.exit = callback;
+ }
+
+ public void setCommandPrefix(final String prefix) {
+ this.commandPrefix = prefix;
+ }
+
+ /**
+ * Set the command line to be evaluated by this command.
+ * <p>
+ * If this command is being invoked from a higher level
+ * {@link DispatchCommand} then only the portion after the command name (that
+ * is, the arguments) is supplied.
+ *
+ * @param line the command line received from the client.
+ */
+ public void setCommandLine(final String line) {
+ this.commandLine = line;
+ }
+
+ /**
+ * Pass all state into the command, then run its start method.
+ * <p>
+ * This method copies all critical state, like the input and output streams,
+ * into the supplied command. The caller must still invoke {@code cmd.start()}
+ * if wants to pass control to the command.
+ *
+ * @param cmd the command that will receive the current state.
+ */
+ protected void provideStateTo(final Command cmd) {
+ cmd.setInputStream(in);
+ cmd.setOutputStream(out);
+ cmd.setErrorStream(err);
+ cmd.setExitCallback(exit);
+ }
+
+ /**
+ * Parses the command line argument, injecting parsed values into fields.
+ * <p>
+ * This method must be explicitly invoked to cause a parse. When parsing,
+ * arguments are split out of and read from the {@link #commandLine} field.
+ *
+ * @throws Failure if the command line arguments were invalid.
+ * @see Option
+ * @see Argument
+ */
+ protected void parseCommandLine() throws Failure {
+ final List<String> list = new ArrayList<String>();
+ boolean inquote = false;
+ StringBuilder r = new StringBuilder();
+ for (int ip = 0; ip < commandLine.length();) {
+ final char b = commandLine.charAt(ip++);
+ switch (b) {
+ case '\t':
+ case ' ':
+ if (inquote)
+ r.append(b);
+ else if (r.length() > 0) {
+ list.add(r.toString());
+ r = new StringBuilder();
+ }
+ continue;
+ case '\'':
+ inquote = !inquote;
+ continue;
+ case '\\':
+ if (inquote || ip == commandLine.length())
+ r.append(b); // literal within a quote
+ else
+ r.append(commandLine.charAt(ip++));
+ continue;
+ default:
+ r.append(b);
+ continue;
+ }
+ }
+ if (r.length() > 0) {
+ list.add(r.toString());
+ }
+
+ final CmdLineParser clp = newCmdLineParser();
+ try {
+ clp.parseArgument(list.toArray(new String[list.size()]));
+ } catch (IllegalArgumentException err) {
+ if (!help) {
+ throw new UnloggedFailure(1, "fatal: " + err.getMessage());
+ }
+ } catch (CmdLineException err) {
+ if (!help) {
+ throw new UnloggedFailure(1, "fatal: " + err.getMessage());
+ }
+ }
+
+ if (help) {
+ final StringWriter msg = new StringWriter();
+ msg.write(commandPrefix);
+ clp.printSingleLineUsage(msg, null);
+ msg.write('\n');
+
+ msg.write('\n');
+ clp.printUsage(msg, null);
+ msg.write('\n');
+ throw new UnloggedFailure(1, msg.toString());
+ }
+ }
+
+ /** Construct a new parser for this command's received command line. */
+ protected CmdLineParser newCmdLineParser() {
+ return cmdLineParserFactory.create(this);
+ }
+
+ /**
+ * Spawn a function into its own thread.
+ * <p>
+ * Typically this should be invoked within {@link Command#start()}, such as:
+ *
+ * <pre>
+ * startThread(new Runnable() {
+ * public void run() {
+ * runImp();
+ * }
+ * });
+ * </pre>
+ *
+ * @param thunk the runnable to execute on the thread, performing the
+ * command's logic.
+ */
+ protected void startThread(final Runnable thunk) {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ thunk.run();
+ }
+ });
+ }
+
+ /**
+ * Spawn a function into its own thread.
+ * <p>
+ * Typically this should be invoked within {@link Command#start()}, such as:
+ *
+ * <pre>
+ * startThread(new CommandRunnable() {
+ * public void run() throws Exception {
+ * runImp();
+ * }
+ * });
+ * </pre>
+ * <p>
+ * If the function throws an exception, it is translated to a simple message
+ * for the client, a non-zero exit code, and the stack trace is logged.
+ *
+ * @param thunk the runnable to execute on the thread, performing the
+ * command's logic.
+ */
+ protected void startThread(final CommandRunnable thunk) {
+ final Context context = SshScopes.getContext();
+ final List<Command> active = context.session.getAttribute(SshUtil.ACTIVE);
+ final Command cmd = this;
+ new Thread(threadName()) {
+ @Override
+ public void run() {
+ int rc = 0;
+ try {
+ synchronized (active) {
+ active.add(cmd);
+ }
+ SshScopes.current.set(context);
+ try {
+ thunk.run();
+ } catch (NoSuchProjectException e) {
+ throw new UnloggedFailure(1, e.getMessage() + " no such project");
+ } catch (NoSuchChangeException e) {
+ throw new UnloggedFailure(1, e.getMessage() + " no such change");
+ }
+ out.flush();
+ err.flush();
+ } catch (Throwable e) {
+ try {
+ out.flush();
+ } catch (Throwable e2) {
+ }
+ try {
+ err.flush();
+ } catch (Throwable e2) {
+ }
+ rc = handleError(e);
+ } finally {
+ synchronized (active) {
+ active.remove(cmd);
+ }
+ onExit(rc);
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Terminate this command and return a result code to the remote client.
+ * <p>
+ * Commands should invoke this at most once. Once invoked, the command may
+ * lose access to request based resources as any callbacks previously
+ * registered with {@link RequestCleanup} will fire.
+ *
+ * @param rc exit code for the remote client.
+ */
+ protected void onExit(final int rc) {
+ exit.onExit(rc);
+ cleanup.run();
+ }
+
+ /** Wrap the supplied output stream in a UTF-8 encoded PrintWriter. */
+ protected static PrintWriter toPrintWriter(final OutputStream o) {
+ try {
+ return new PrintWriter(new BufferedWriter(new OutputStreamWriter(o, ENC)));
+ } catch (UnsupportedEncodingException e) {
+ // Our default encoding is required by the specifications for the
+ // runtime APIs, this should never, ever happen.
+ //
+ throw new RuntimeException("JVM lacks " + ENC + " encoding", e);
+ }
+ }
+
+ private String threadName() {
+ final ServerSession session = SshScopes.getContext().session;
+ final String who = session.getUsername();
+ final Account.Id id = session.getAttribute(SshUtil.CURRENT_ACCOUNT);
+ return "SSH " + getFullCommandLine() + " / " + who + " " + id;
+ }
+
+ private int handleError(final Throwable e) {
+ if (e.getClass() == IOException.class
+ && "Pipe closed".equals(e.getMessage())) {
+ // This is sshd telling us the client just dropped off while
+ // we were waiting for a read or a write to complete. Either
+ // way its not really a fatal error. Don't log it.
+ //
+ return 127;
+ }
+
+ if (e.getClass() == SshException.class
+ && "Already closed".equals(e.getMessage())) {
+ // This is sshd telling us the client just dropped off while
+ // we were waiting for a read or a write to complete. Either
+ // way its not really a fatal error. Don't log it.
+ //
+ return 127;
+ }
+
+ if (e instanceof UnloggedFailure) {
+ } else {
+ final ServerSession session = SshScopes.getContext().session;
+ final StringBuilder m = new StringBuilder();
+ m.append("Internal server error (");
+ m.append("user ");
+ m.append(session.getUsername());
+ m.append(" account ");
+ m.append(session.getAttribute(SshUtil.CURRENT_ACCOUNT));
+ m.append(") during ");
+ m.append(getFullCommandLine());
+ log.error(m.toString(), e);
+ }
+
+ if (e instanceof Failure) {
+ final Failure f = (Failure) e;
+ try {
+ err.write((f.getMessage() + "\n").getBytes(ENC));
+ err.flush();
+ } catch (IOException e2) {
+ } catch (Throwable e2) {
+ log.warn("Cannot send failure message to client", e2);
+ }
+ return f.exitCode;
+
+ } else {
+ try {
+ err.write("fatal: internal server error\n".getBytes(ENC));
+ err.flush();
+ } catch (IOException e2) {
+ } catch (Throwable e2) {
+ log.warn("Cannot send internal server error message to client", e2);
+ }
+ return 128;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getFullCommandLine();
+ }
+
+ private String getFullCommandLine() {
+ if (commandPrefix.isEmpty())
+ return commandLine;
+ else if (commandLine.isEmpty())
+ return commandPrefix;
+ else
+ return commandPrefix + " " + commandLine;
+ }
+
+ /** Runnable function which can throw an exception. */
+ public static interface CommandRunnable {
+ public void run() throws Exception;
+ }
+
+ /** Thrown from {@link CommandRunnable#run()} with client message and code. */
+ public static class Failure extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ final int exitCode;
+
+ /**
+ * Create a new failure.
+ *
+ * @param exitCode exit code to return the client, which indicates the
+ * failure status of this command. Should be between 1 and 255,
+ * inclusive.
+ * @param msg message to also send to the client's stderr.
+ */
+ public Failure(final int exitCode, final String msg) {
+ this(exitCode, msg, null);
+ }
+
+ /**
+ * Create a new failure.
+ *
+ * @param exitCode exit code to return the client, which indicates the
+ * failure status of this command. Should be between 1 and 255,
+ * inclusive.
+ * @param msg message to also send to the client's stderr.
+ * @param why stack trace to include in the server's log, but is not sent to
+ * the client's stderr.
+ */
+ public Failure(final int exitCode, final String msg, final Throwable why) {
+ super(msg, why);
+ this.exitCode = exitCode;
+ }
+ }
+
+ /** Thrown from {@link CommandRunnable#run()} with client message and code. */
+ public static class UnloggedFailure extends Failure {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new failure.
+ *
+ * @param exitCode exit code to return the client, which indicates the
+ * failure status of this command. Should be between 1 and 255,
+ * inclusive.
+ * @param msg message to also send to the client's stderr.
+ */
+ public UnloggedFailure(final int exitCode, final String msg) {
+ this(exitCode, msg, null);
+ }
+
+ /**
+ * Create a new failure.
+ *
+ * @param exitCode exit code to return the client, which indicates the
+ * failure status of this command. Should be between 1 and 255,
+ * inclusive.
+ * @param msg message to also send to the client's stderr.
+ * @param why stack trace to include in the server's log, but is not sent to
+ * the client's stderr.
+ */
+ public UnloggedFailure(final int exitCode, final String msg,
+ final Throwable why) {
+ super(exitCode, msg, why);
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
new file mode 100644
index 0000000000..53279c88b2
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandFactoryProvider.java
@@ -0,0 +1,100 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.sshd.SshScopes.Context;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.CommandFactory.Command;
+import org.apache.sshd.server.CommandFactory.ExitCallback;
+import org.apache.sshd.server.CommandFactory.SessionAware;
+import org.apache.sshd.server.session.ServerSession;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Creates a CommandFactory using commands registered by {@link CommandModule}.
+ */
+class CommandFactoryProvider implements Provider<CommandFactory> {
+ private final DispatchCommandProvider dispatcher;
+
+ @Inject
+ CommandFactoryProvider(
+ @CommandName(Commands.ROOT) final DispatchCommandProvider d) {
+ dispatcher = d;
+ }
+
+ @Override
+ public CommandFactory get() {
+ return new CommandFactory() {
+ public Command createCommand(final String requestCommand) {
+ return new Trampoline(requestCommand);
+ }
+ };
+ }
+
+ private class Trampoline implements Command, SessionAware {
+ private final String commandLine;
+ private InputStream in;
+ private OutputStream out;
+ private OutputStream err;
+ private ExitCallback exit;
+ private ServerSession session;
+
+ Trampoline(final String cmdLine) {
+ commandLine = cmdLine;
+ }
+
+ public void setInputStream(final InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(final OutputStream out) {
+ this.out = out;
+ }
+
+ public void setErrorStream(final OutputStream err) {
+ this.err = err;
+ }
+
+ public void setExitCallback(final ExitCallback callback) {
+ this.exit = callback;
+ }
+
+ public void setSession(final ServerSession session) {
+ this.session = session;
+ }
+
+ public void start() throws IOException {
+ final Context old = SshScopes.current.get();
+ try {
+ SshScopes.current.set(new Context(session));
+ final DispatchCommand c = dispatcher.get();
+ c.setCommandLine(commandLine);
+ c.setInputStream(in);
+ c.setOutputStream(out);
+ c.setErrorStream(err);
+ c.setExitCallback(exit);
+ c.start();
+ } finally {
+ SshScopes.current.set(old);
+ }
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java
new file mode 100644
index 0000000000..857e118a8d
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandModule.java
@@ -0,0 +1,74 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.binder.LinkedBindingBuilder;
+
+import org.apache.sshd.server.CommandFactory.Command;
+
+/** Module to register commands in the SSH daemon. */
+public abstract class CommandModule extends AbstractModule {
+ /**
+ * Configure a command to be invoked by name.
+ *
+ * @param name the name of the command the client will provide in order to
+ * call the command.
+ * @return a binding that must be bound to a non-singleton provider for a
+ * {@link Command} object.
+ */
+ protected LinkedBindingBuilder<Command> command(final String name) {
+ return bind(Commands.key(name));
+ }
+
+ /**
+ * Configure a command to be invoked by name.
+ *
+ * @param name the name of the command the client will provide in order to
+ * call the command.
+ * @return a binding that must be bound to a non-singleton provider for a
+ * {@link Command} object.
+ */
+ protected LinkedBindingBuilder<Command> command(final CommandName name) {
+ return bind(Commands.key(name));
+ }
+
+ /**
+ * Configure a command to be invoked by name.
+ *
+ *@param parent context of the parent command, that this command is a
+ * subcommand of.
+ * @param name the name of the command the client will provide in order to
+ * call the command.
+ * @return a binding that must be bound to a non-singleton provider for a
+ * {@link Command} object.
+ */
+ protected LinkedBindingBuilder<Command> command(final CommandName parent,
+ final String name) {
+ return bind(Commands.key(parent, name));
+ }
+
+ /**
+ * Alias one command to another.
+ *
+ * @param from the new command name that when called will actually delegate to
+ * {@code to}'s implementation.
+ * @param to name of an already registered command that will perform the
+ * action when {@code from} is invoked by a client.
+ */
+ protected void alias(final String from, final String to) {
+ bind(Commands.key(from)).to(Commands.key(to));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandName.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandName.java
new file mode 100644
index 0000000000..09503bb3ee
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/CommandName.java
@@ -0,0 +1,35 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.google.inject.BindingAnnotation;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Name of a command registered in an SSH daemon.
+ * <p>
+ * Use {@link Commands#key(String)} to construct a key for a command name.
+ *
+ * @see CommandModule#command(String)
+ * @see Commands#key(String)
+ */
+@Retention(RUNTIME)
+@BindingAnnotation
+public @interface CommandName {
+ String value();
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java
new file mode 100644
index 0000000000..86b3522711
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/Commands.java
@@ -0,0 +1,139 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.inject.Key;
+
+import org.apache.sshd.server.CommandFactory;
+
+import java.lang.annotation.Annotation;
+
+/** Utilities to support {@link CommandName} construction. */
+public class Commands {
+ /** Magic value signaling the top level. */
+ public static final String ROOT = "";
+
+ /** Magic value signaling the top level. */
+ public static final CommandName CMD_ROOT = named(ROOT);
+
+ public static Key<CommandFactory.Command> key(final String name) {
+ return key(named(name));
+ }
+
+ public static Key<CommandFactory.Command> key(final CommandName name) {
+ return Key.get(CommandFactory.Command.class, name);
+ }
+
+ public static Key<CommandFactory.Command> key(final CommandName parent,
+ final String name) {
+ return Key.get(CommandFactory.Command.class, named(parent, name));
+ }
+
+ /** Create a CommandName annotation for the supplied name. */
+ public static CommandName named(final String name) {
+ return new CommandName() {
+ @Override
+ public String value() {
+ return name;
+ }
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return CommandName.class;
+ }
+
+ @Override
+ public int hashCode() {
+ // This is specified in java.lang.Annotation.
+ return (127 * "value".hashCode()) ^ value().hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof CommandName
+ && value().equals(((CommandName) obj).value());
+ }
+
+ @Override
+ public String toString() {
+ return "@" + CommandName.class.getName() + "(value=" + value() + ")";
+ }
+ };
+ }
+
+ /** Create a CommandName annotation for the supplied name. */
+ public static CommandName named(final CommandName parent, final String name) {
+ return new NestedCommandNameImpl(parent, name);
+ }
+
+ /** Return the name of this command, possibly including any parents. */
+ public static String nameOf(final CommandName name) {
+ if (name instanceof NestedCommandNameImpl) {
+ return nameOf(((NestedCommandNameImpl) name).parent) + " " + name.value();
+ }
+ return name.value();
+ }
+
+ /** Is the second command a direct child of the first command? */
+ public static boolean isChild(final CommandName parent, final CommandName name) {
+ if (name instanceof NestedCommandNameImpl) {
+ return parent.equals(((NestedCommandNameImpl) name).parent);
+ }
+ if (parent == CMD_ROOT) {
+ return true;
+ }
+ return false;
+ }
+
+ private static final class NestedCommandNameImpl implements CommandName {
+ private final CommandName parent;
+ private final String name;
+
+ NestedCommandNameImpl(final CommandName parent, final String name) {
+ this.parent = parent;
+ this.name = name;
+ }
+
+ @Override
+ public String value() {
+ return name;
+ }
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return CommandName.class;
+ }
+
+ @Override
+ public int hashCode() {
+ return parent.hashCode() * 31 + value().hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj instanceof NestedCommandNameImpl
+ && parent.equals(((NestedCommandNameImpl) obj).parent)
+ && value().equals(((NestedCommandNameImpl) obj).value());
+ }
+
+ @Override
+ public String toString() {
+ return "CommandName[" + nameOf(this) + "]";
+ }
+ }
+
+ private Commands() {
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
new file mode 100644
index 0000000000..45d4ab2009
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+
+import java.security.PublicKey;
+
+/**
+ * Authenticates by public key through {@link AccountSshKey} entities.
+ * <p>
+ * The username supplied by the client must be the user's preferred email
+ * address, as listed in their Account entity. Only keys listed under that
+ * account as authorized keys are permitted to access the account.
+ */
+@Singleton
+class DatabasePubKeyAuth implements PublickeyAuthenticator {
+ private final SshKeyCacheImpl sshKeyCache;
+ private final SchemaFactory<ReviewDb> schema;
+
+ @Inject
+ DatabasePubKeyAuth(final SshKeyCacheImpl skc, final SchemaFactory<ReviewDb> sf) {
+ sshKeyCache = skc;
+ schema = sf;
+ }
+
+ public boolean hasKey(final String username, final PublicKey suppliedKey,
+ final ServerSession session) {
+ final Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
+ final SshKeyCacheEntry key = find(keyList, suppliedKey);
+ if (key == null) {
+ return false;
+ }
+
+ // Double check that all of the keys are for the same user account.
+ // This should have been true when the cache factory method loaded
+ // the list into memory, but we want to be extra paranoid about our
+ // security check to ensure there aren't two users sharing the same
+ // user name on the server.
+ //
+ for (final SshKeyCacheEntry otherKey : keyList) {
+ if (!key.getAccount().equals(otherKey.getAccount())) {
+ return false;
+ }
+ }
+
+ key.updateLastUsed(schema);
+ session.setAttribute(SshUtil.CURRENT_ACCOUNT, key.getAccount());
+ return true;
+ }
+
+ private SshKeyCacheEntry find(final Iterable<SshKeyCacheEntry> keyList,
+ final PublicKey suppliedKey) {
+ for (final SshKeyCacheEntry k : keyList) {
+ if (k.match(suppliedKey)) {
+ return k;
+ }
+ }
+ return null;
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
new file mode 100644
index 0000000000..7ff527b1c5
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommand.java
@@ -0,0 +1,130 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
+
+import org.apache.sshd.server.CommandFactory.Command;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+/**
+ * Command that dispatches to a subcommand from its command table.
+ */
+final class DispatchCommand extends BaseCommand {
+ interface Factory {
+ DispatchCommand create(String prefix, Map<String, Provider<Command>> map);
+ }
+
+ private final Provider<CurrentUser> currentUser;
+ private final String prefix;
+ private final Map<String, Provider<Command>> commands;
+
+ @Inject
+ DispatchCommand(final Provider<CurrentUser> cu, @Assisted final String pfx,
+ @Assisted final Map<String, Provider<Command>> all) {
+ currentUser = cu;
+ prefix = pfx;
+ commands = all;
+ }
+
+ @Override
+ public void start() throws IOException {
+ if (commandLine.isEmpty()) {
+ usage();
+ return;
+ }
+
+ final String name, args;
+ int sp = commandLine.indexOf(' ');
+ if (0 < sp) {
+ name = commandLine.substring(0, sp);
+ while (Character.isWhitespace(commandLine.charAt(sp))) {
+ sp++;
+ }
+ args = commandLine.substring(sp);
+ } else {
+ name = commandLine;
+ args = "";
+ }
+
+ if (name.equals("help") || name.equals("--help") || name.equals("-h")) {
+ usage();
+ return;
+ }
+
+ final Provider<Command> p = commands.get(name);
+ if (p != null) {
+ final Command cmd = p.get();
+ if (cmd.getClass().getAnnotation(AdminCommand.class) != null) {
+ final CurrentUser u = currentUser.get();
+ if (!u.isAdministrator()) {
+ err.write("fatal: Not a Gerrit administrator\n".getBytes(ENC));
+ err.flush();
+ onExit(1);
+ return;
+ }
+ }
+
+ provideStateTo(cmd);
+ if (cmd instanceof BaseCommand) {
+ final BaseCommand bc = (BaseCommand) cmd;
+ if (commandPrefix.isEmpty())
+ bc.setCommandPrefix(name);
+ else
+ bc.setCommandPrefix(commandPrefix + " " + name);
+ bc.setCommandLine(args);
+ }
+ cmd.start();
+ } else {
+ final String msg = prefix + ": " + name + ": not found\n";
+ err.write(msg.getBytes(ENC));
+ err.flush();
+ onExit(127);
+ }
+ }
+
+ private void usage() throws IOException, UnsupportedEncodingException {
+ final StringBuilder usage = new StringBuilder();
+ if (prefix.indexOf(' ') < 0) {
+ usage.append("usage: " + prefix + " COMMAND [ARGS]\n");
+ }
+ usage.append("\n");
+ usage.append("Available commands of " + prefix + " are:\n");
+ usage.append("\n");
+ for (Map.Entry<String, Provider<Command>> e : commands.entrySet()) {
+ usage.append(" ");
+ usage.append(e.getKey());
+ usage.append("\n");
+ }
+ usage.append("\n");
+
+ usage.append("See '");
+ if (prefix.indexOf(' ') < 0) {
+ usage.append(prefix);
+ usage.append(' ');
+ }
+ usage.append("COMMAND --help' for more information.\n");
+ usage.append("\n");
+ err.write(usage.toString().getBytes("UTF-8"));
+ err.flush();
+ onExit(1);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommandProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommandProvider.java
new file mode 100644
index 0000000000..abd2a218e3
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DispatchCommandProvider.java
@@ -0,0 +1,97 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.inject.Binding;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+
+import org.apache.sshd.server.CommandFactory.Command;
+
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Creates DispatchCommand using commands registered by {@link CommandModule}.
+ */
+public class DispatchCommandProvider implements Provider<DispatchCommand> {
+ @Inject
+ private Injector injector;
+
+ @Inject
+ private DispatchCommand.Factory factory;
+
+ private final String dispatcherName;
+ private final CommandName parent;
+
+ private volatile Map<String, Provider<Command>> map;
+
+ public DispatchCommandProvider(final CommandName cn) {
+ this(Commands.nameOf(cn), cn);
+ }
+
+ public DispatchCommandProvider(final String dispatcherName,
+ final CommandName cn) {
+ this.dispatcherName = dispatcherName;
+ this.parent = cn;
+ }
+
+ @Override
+ public DispatchCommand get() {
+ return factory.create(dispatcherName, getMap());
+ }
+
+ private Map<String, Provider<Command>> getMap() {
+ if (map == null) {
+ synchronized (this) {
+ if (map == null) {
+ map = createMap();
+ }
+ }
+ }
+ return map;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Provider<Command>> createMap() {
+ final Map<String, Provider<Command>> m =
+ new TreeMap<String, Provider<Command>>();
+
+ for (final Binding<?> b : allCommands()) {
+ final Annotation annotation = b.getKey().getAnnotation();
+ if (annotation instanceof CommandName) {
+ final CommandName n = (CommandName) annotation;
+ if (!Commands.CMD_ROOT.equals(n) && Commands.isChild(parent, n)) {
+ m.put(n.value(), (Provider<Command>) b.getProvider());
+ }
+ }
+ }
+
+ return Collections.unmodifiableMap(new LinkedHashMap(m));
+ }
+
+ private static final TypeLiteral<Command> type =
+ new TypeLiteral<Command>() {};
+
+ private List<Binding<Command>> allCommands() {
+ return injector.findBindingsByType(type);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java
new file mode 100644
index 0000000000..db84cb79ac
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/HostKeyProvider.java
@@ -0,0 +1,77 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.server.config.SitePath;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+class HostKeyProvider implements Provider<KeyPairProvider> {
+ private final File sitePath;
+
+ @Inject
+ HostKeyProvider(@SitePath final File sitePath) {
+ this.sitePath = sitePath;
+ }
+
+ @Override
+ public KeyPairProvider get() {
+ final File anyKey = new File(sitePath, "ssh_host_key");
+ final File rsaKey = new File(sitePath, "ssh_host_rsa_key");
+ final File dsaKey = new File(sitePath, "ssh_host_dsa_key");
+
+ final List<String> keys = new ArrayList<String>(2);
+ if (rsaKey.exists()) {
+ keys.add(rsaKey.getAbsolutePath());
+ }
+ if (dsaKey.exists()) {
+ keys.add(dsaKey.getAbsolutePath());
+ }
+
+ if (anyKey.exists() && !keys.isEmpty()) {
+ // If both formats of host key exist, we don't know which format
+ // should be authoritative. Complain and abort.
+ //
+ keys.add(anyKey.getAbsolutePath());
+ throw new ProvisionException("Multiple host keys exist: " + keys);
+ }
+
+ if (keys.isEmpty()) {
+ // No administrator created host key? Generate and save our own.
+ //
+ final SimpleGeneratorHostKeyProvider keyp;
+
+ keyp = new SimpleGeneratorHostKeyProvider();
+ keyp.setPath(anyKey.getAbsolutePath());
+ return keyp;
+ }
+
+ if (!SecurityUtils.isBouncyCastleRegistered()) {
+ throw new ProvisionException("Bouncy Castle Crypto not installed;"
+ + " needed to read server host keys: " + keys + "");
+ }
+ return new FileKeyPairProvider(keys.toArray(new String[keys.size()]));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
new file mode 100644
index 0000000000..5cf9cb7681
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/NoShell.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import org.apache.sshd.server.ShellFactory;
+import org.eclipse.jgit.lib.Constants;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Dummy shell which prints a message and terminates.
+ * <p>
+ * This implementation is used to ensure clients who try to SSH directly to this
+ * server without supplying a command will get a reasonable error message, but
+ * cannot continue further.
+ */
+class NoShell implements ShellFactory {
+ public Shell createShell() {
+ return new Shell() {
+ private InputStream in;
+ private OutputStream out;
+ private OutputStream err;
+ private ExitCallback exit;
+
+ public void setInputStream(final InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(final OutputStream out) {
+ this.out = out;
+ }
+
+ public void setErrorStream(final OutputStream err) {
+ this.err = err;
+ }
+
+ public void setExitCallback(final ExitCallback callback) {
+ this.exit = callback;
+ }
+
+ public void start(final Environment env) throws IOException {
+ err.write(Constants.encodeASCII("gerrit: no shell available\n"));
+ in.close();
+ out.close();
+ err.close();
+ exit.onExit(127);
+ }
+
+ public void destroy() {
+ }
+ };
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshCurrentUserProvider.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshCurrentUserProvider.java
new file mode 100644
index 0000000000..9a0b233474
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshCurrentUserProvider.java
@@ -0,0 +1,44 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.AccessPath;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.sshd.SshScopes.Context;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.Singleton;
+
+@Singleton
+class SshCurrentUserProvider implements Provider<IdentifiedUser> {
+ private final IdentifiedUser.RequestFactory factory;
+
+ @Inject
+ SshCurrentUserProvider(final IdentifiedUser.RequestFactory f) {
+ factory = f;
+ }
+
+ @Override
+ public IdentifiedUser get() {
+ final Context ctx = SshScopes.getContext();
+ final Account.Id id = ctx.session.getAttribute(SshUtil.CURRENT_ACCOUNT);
+ if (id == null) {
+ throw new ProvisionException("User not yet authenticated");
+ }
+ return factory.create(AccessPath.SSH, id);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
new file mode 100644
index 0000000000..3477b536f6
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -0,0 +1,503 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.inject.Inject;
+import com.google.inject.Key;
+import com.google.inject.Singleton;
+
+import com.jcraft.jsch.HostKey;
+import com.jcraft.jsch.JSchException;
+
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.SocketSessionConfig;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.sshd.SshServer;
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.Compression;
+import org.apache.sshd.common.KeyExchange;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.cipher.AES128CBC;
+import org.apache.sshd.common.cipher.AES192CBC;
+import org.apache.sshd.common.cipher.AES256CBC;
+import org.apache.sshd.common.cipher.BlowfishCBC;
+import org.apache.sshd.common.cipher.CipherNone;
+import org.apache.sshd.common.cipher.TripleDESCBC;
+import org.apache.sshd.common.compression.CompressionNone;
+import org.apache.sshd.common.mac.HMACMD5;
+import org.apache.sshd.common.mac.HMACMD596;
+import org.apache.sshd.common.mac.HMACSHA1;
+import org.apache.sshd.common.mac.HMACSHA196;
+import org.apache.sshd.common.random.BouncyCastleRandom;
+import org.apache.sshd.common.random.JceRandom;
+import org.apache.sshd.common.random.SingletonRandomFactory;
+import org.apache.sshd.common.signature.SignatureDSA;
+import org.apache.sshd.common.signature.SignatureRSA;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.util.SecurityUtils;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.ServerChannel;
+import org.apache.sshd.server.SessionFactory;
+import org.apache.sshd.server.UserAuth;
+import org.apache.sshd.server.CommandFactory.Command;
+import org.apache.sshd.server.auth.UserAuthPublicKey;
+import org.apache.sshd.server.channel.ChannelSession;
+import org.apache.sshd.server.kex.DHG1;
+import org.apache.sshd.server.kex.DHG14;
+import org.apache.sshd.server.session.ServerSession;
+import org.eclipse.jgit.lib.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * SSH daemon to communicate with Gerrit.
+ * <p>
+ * Use a Git URL such as <code>ssh://${email}@${host}:${port}/${path}</code>,
+ * e.g. <code>ssh://sop@google.com@gerrit.com:8010/tools/gerrit.git</code> to
+ * access the SSH daemon itself.
+ * <p>
+ * Versions of Git before 1.5.3 may require setting the username and port
+ * properties in the user's <code>~/.ssh/config</code> file, and using a host
+ * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git:
+ * <pre>
+ * Host gerrit-alias
+ * User sop@google.com
+ * Hostname gerrit.com
+ * Port 8010
+ * </pre>
+ */
+@Singleton
+public class SshDaemon extends SshServer implements SshInfo {
+ private static final int IANA_SSH_PORT = 22;
+ private static final int DEFAULT_PORT = 29418;
+
+ private static final Logger log = LoggerFactory.getLogger(SshDaemon.class);
+
+ private static String format(final SocketAddress addr) {
+ if (addr instanceof InetSocketAddress) {
+ final InetSocketAddress inetAddr = (InetSocketAddress) addr;
+ final InetAddress hostAddr = inetAddr.getAddress();
+ String host;
+ if (hostAddr.isAnyLocalAddress()) {
+ host = "*";
+ } else if (inetAddr.getPort() == IANA_SSH_PORT && !isIPv6(hostAddr)) {
+ return inetAddr.getHostName();
+ } else {
+ host = "[" + hostAddr.getHostName() + "]";
+ }
+ return host + ":" + inetAddr.getPort();
+ }
+ return addr.toString();
+ }
+
+ private static boolean isIPv6(final InetAddress ip) {
+ return ip instanceof Inet6Address
+ && ip.getHostName().equals(ip.getHostAddress());
+ }
+
+ private final List<SocketAddress> listen;
+ private final boolean reuseAddress;
+ private final boolean keepAlive;
+ private final List<HostKey> hostKeys;
+ private volatile IoAcceptor acceptor;
+
+ @Inject
+ SshDaemon(final CommandFactory commandFactory,
+ final PublickeyAuthenticator userAuth,
+ final KeyPairProvider hostKeyProvider,
+ @GerritServerConfig final Config cfg) {
+ setPort(IANA_SSH_PORT /* never used */);
+
+ listen = parseListen(cfg);
+ reuseAddress = cfg.getBoolean("sshd", "reuseaddress", true);
+ keepAlive = cfg.getBoolean("sshd", "tcpkeepalive", true);
+
+ if (SecurityUtils.isBouncyCastleRegistered()) {
+ initProviderBouncyCastle();
+ } else {
+ initProviderJce();
+ }
+ initCiphers(cfg);
+ initMacs(cfg);
+ initSignatures();
+ initChannels();
+ initCompression();
+ initUserAuth(userAuth);
+ setKeyPairProvider(hostKeyProvider);
+ setCommandFactory(commandFactory);
+ setShellFactory(new NoShell());
+ setSessionFactory(new SessionFactory() {
+ @Override
+ protected ServerSession createSession(final IoSession io)
+ throws Exception {
+ if (io.getConfig() instanceof SocketSessionConfig) {
+ final SocketSessionConfig c = (SocketSessionConfig) io.getConfig();
+ c.setKeepAlive(keepAlive);
+ }
+
+ final ServerSession s = (ServerSession) super.createSession(io);
+ s.setAttribute(SshUtil.REMOTE_PEER, io.getRemoteAddress());
+ s.setAttribute(SshUtil.ACTIVE, new ArrayList<Command>(2));
+ s.setAttribute(SshScopes.sessionMap, new HashMap<Key<?>, Object>());
+ return s;
+ }
+ });
+
+ hostKeys = computeHostKeys();
+ }
+
+ @Override
+ public List<HostKey> getHostKeys() {
+ return hostKeys;
+ }
+
+ public IoAcceptor getIoAcceptor() {
+ return acceptor;
+ }
+
+ @Override
+ public synchronized void start() throws IOException {
+ if (acceptor == null) {
+ checkConfig();
+
+ final NioSocketAcceptor ain = new NioSocketAcceptor();
+ final SessionFactory handler = getSessionFactory();
+ handler.setServer(this);
+ ain.setHandler(handler);
+ ain.setReuseAddress(reuseAddress);
+ ain.bind(listen);
+ acceptor = ain;
+
+ log.info("Started Gerrit SSHD on " + addressList());
+ }
+ }
+
+ @Override
+ public synchronized void stop() {
+ if (acceptor != null) {
+ try {
+ acceptor.dispose();
+ log.info("Stopped Gerrit SSHD");
+ } finally {
+ acceptor = null;
+ }
+ }
+ }
+
+ @Override
+ protected void checkConfig() {
+ super.checkConfig();
+ if (myHostKeys().isEmpty()) {
+ throw new IllegalStateException("No SSHD host key");
+ }
+ }
+
+ private List<HostKey> computeHostKeys() {
+ final List<PublicKey> keys = myHostKeys();
+ final ArrayList<HostKey> r = new ArrayList<HostKey>();
+ for (final PublicKey pub : keys) {
+ final Buffer buf = new Buffer();
+ buf.putPublicKey(pub);
+ final byte[] keyBin = buf.getCompactData();
+
+ for (final SocketAddress addr : listen) {
+ if (!(addr instanceof InetSocketAddress)) {
+ continue;
+ }
+
+ final InetSocketAddress inetAddr = (InetSocketAddress) addr;
+ if (inetAddr.getAddress().isLoopbackAddress()) {
+ continue;
+ }
+
+ try {
+ r.add(new HostKey(format(inetAddr), keyBin));
+ } catch (JSchException e) {
+ log.warn("Cannot format SSHD host key", e);
+ }
+ }
+ }
+ return Collections.unmodifiableList(r);
+ }
+
+ private List<PublicKey> myHostKeys() {
+ final KeyPairProvider p = getKeyPairProvider();
+ final List<PublicKey> keys = new ArrayList<PublicKey>(2);
+ addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
+ addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
+ return keys;
+ }
+
+ private static void addPublicKey(final Collection<PublicKey> out,
+ final KeyPairProvider p, final String type) {
+ final KeyPair pair = p.loadKey(type);
+ if (pair != null && pair.getPublic() != null) {
+ out.add(pair.getPublic());
+ }
+ }
+
+ private String addressList() {
+ final StringBuilder r = new StringBuilder();
+ for (Iterator<SocketAddress> i = listen.iterator(); i.hasNext();) {
+ r.append(format(i.next()));
+ if (i.hasNext()) {
+ r.append(", ");
+ }
+ }
+ return r.toString();
+ }
+
+ private List<SocketAddress> parseListen(final Config cfg) {
+ final ArrayList<SocketAddress> bind = new ArrayList<SocketAddress>(2);
+ final String[] want = cfg.getStringList("sshd", null, "listenaddress");
+ if (want == null || want.length == 0) {
+ bind.add(new InetSocketAddress(DEFAULT_PORT));
+ return bind;
+ }
+
+ for (final String desc : want) {
+ try {
+ bind.add(toSocketAddress(desc));
+ } catch (IllegalArgumentException e) {
+ log.error("Bad sshd.listenaddress: " + desc + ": " + e.getMessage());
+ }
+ }
+ return bind;
+ }
+
+ private SocketAddress toSocketAddress(final String desc) {
+ String hostStr;
+ String portStr;
+
+ if (desc.startsWith("[")) {
+ // IPv6, as a raw IP address.
+ //
+ final int hostEnd = desc.indexOf(']');
+ if (hostEnd < 0) {
+ throw new IllegalArgumentException("invalid IPv6 representation");
+ }
+
+ hostStr = desc.substring(1, hostEnd);
+ portStr = desc.substring(hostEnd + 1);
+ } else {
+ // IPv4, or a host name.
+ //
+ final int hostEnd = desc.indexOf(':');
+ hostStr = 0 <= hostEnd ? desc.substring(0, hostEnd) : desc;
+ portStr = 0 <= hostEnd ? desc.substring(hostEnd) : "";
+ }
+
+ if ("*".equals(hostStr)) {
+ hostStr = "";
+ }
+ if (portStr.startsWith(":")) {
+ portStr = portStr.substring(1);
+ }
+
+ final int port;
+ if (portStr.length() > 0) {
+ try {
+ port = Integer.parseInt(portStr);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("invalid port");
+ }
+ } else {
+ port = DEFAULT_PORT;
+ }
+
+ if (hostStr.length() > 0) {
+ try {
+ return new InetSocketAddress(InetAddress.getByName(hostStr), port);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ } else {
+ return new InetSocketAddress(port);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initProviderBouncyCastle() {
+ setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>> asList(
+ new DHG14.Factory(), new DHG1.Factory()));
+ setRandomFactory(new SingletonRandomFactory(
+ new BouncyCastleRandom.Factory()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initProviderJce() {
+ setKeyExchangeFactories(Arrays
+ .<NamedFactory<KeyExchange>> asList(new DHG1.Factory()));
+ setRandomFactory(new SingletonRandomFactory(new JceRandom.Factory()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initCiphers(final Config cfg) {
+ final List<NamedFactory<Cipher>> a = new LinkedList<NamedFactory<Cipher>>();
+ a.add(new AES128CBC.Factory());
+ a.add(new TripleDESCBC.Factory());
+ a.add(new BlowfishCBC.Factory());
+ a.add(new AES192CBC.Factory());
+ a.add(new AES256CBC.Factory());
+
+ for (Iterator<NamedFactory<Cipher>> i = a.iterator(); i.hasNext();) {
+ final NamedFactory<Cipher> f = i.next();
+ try {
+ final Cipher c = f.create();
+ final byte[] key = new byte[c.getBlockSize()];
+ final byte[] iv = new byte[c.getIVSize()];
+ c.init(Cipher.Mode.Encrypt, key, iv);
+ } catch (InvalidKeyException e) {
+ log.warn("Disabling cipher " + f.getName() + ": " + e.getMessage()
+ + "; try installing unlimited cryptography extension");
+ i.remove();
+ } catch (Exception e) {
+ log.warn("Disabling cipher " + f.getName() + ": " + e.getMessage());
+ i.remove();
+ }
+ }
+
+ a.add(null);
+ a.add(new CipherNone.Factory());
+ setCipherFactories(filter(cfg, "cipher", a.toArray(new NamedFactory[a
+ .size()])));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initMacs(final Config cfg) {
+ setMacFactories(filter(cfg, "mac", new HMACMD5.Factory(),
+ new HMACSHA1.Factory(), new HMACMD596.Factory(),
+ new HMACSHA196.Factory()));
+ }
+
+ private static <T> List<NamedFactory<T>> filter(final Config cfg,
+ final String key, final NamedFactory<T>... avail) {
+ final ArrayList<NamedFactory<T>> def = new ArrayList<NamedFactory<T>>();
+ for (final NamedFactory<T> n : avail) {
+ if (n == null) {
+ break;
+ }
+ def.add(n);
+ }
+
+ final String[] want = cfg.getStringList("sshd", null, key);
+ if (want == null || want.length == 0) {
+ return def;
+ }
+
+ boolean didClear = false;
+ for (final String setting : want) {
+ String name = setting.trim();
+ boolean add = true;
+ if (name.startsWith("-")) {
+ add = false;
+ name = name.substring(1).trim();
+ } else if (name.startsWith("+")) {
+ name = name.substring(1).trim();
+ } else if (!didClear) {
+ didClear = true;
+ def.clear();
+ }
+
+ final NamedFactory<T> n = find(name, avail);
+ if (n == null) {
+ final StringBuilder msg = new StringBuilder();
+ msg.append("sshd." + key + " = " + name + " unsupported; only ");
+ for (int i = 0; i < avail.length; i++) {
+ if (avail[i] == null) {
+ continue;
+ }
+ if (i > 0) {
+ msg.append(", ");
+ }
+ msg.append(avail[i].getName());
+ }
+ msg.append(" is supported");
+ log.error(msg.toString());
+ } else if (add) {
+ if (!def.contains(n)) {
+ def.add(n);
+ }
+ } else {
+ def.remove(n);
+ }
+ }
+
+ return def;
+ }
+
+ private static <T> NamedFactory<T> find(final String name,
+ final NamedFactory<T>... avail) {
+ for (final NamedFactory<T> n : avail) {
+ if (n != null && name.equals(n.getName())) {
+ return n;
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initSignatures() {
+ setSignatureFactories(Arrays.<NamedFactory<Signature>> asList(
+ new SignatureDSA.Factory(), new SignatureRSA.Factory()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initCompression() {
+ // Always disable transparent compression. The majority of our data
+ // transfer is highly compressed Git pack files. We cannot make them
+ // any smaller than they already are.
+ //
+ setCompressionFactories(Arrays
+ .<NamedFactory<Compression>> asList(new CompressionNone.Factory()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initChannels() {
+ setChannelFactories(Arrays
+ .<NamedFactory<ServerChannel>> asList(new ChannelSession.Factory()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void initUserAuth(final PublickeyAuthenticator pubkey) {
+ setUserAuthFactories(Arrays
+ .<NamedFactory<UserAuth>> asList(new UserAuthPublicKey.Factory()));
+ setPublickeyAuthenticator(pubkey);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheEntry.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheEntry.java
new file mode 100644
index 0000000000..85bb382422
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheEntry.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.PublicKey;
+import java.util.Collections;
+
+class SshKeyCacheEntry {
+ private static final Logger log =
+ LoggerFactory.getLogger(SshKeyCacheEntry.class);
+
+ private final AccountSshKey.Id id;
+ private final PublicKey publicKey;
+
+ SshKeyCacheEntry(final AccountSshKey.Id i, final PublicKey k) {
+ id = i;
+ publicKey = k;
+ }
+
+ Account.Id getAccount() {
+ return id.getParentKey();
+ }
+
+ boolean match(final PublicKey inkey) {
+ return publicKey.equals(inkey);
+ }
+
+ void updateLastUsed(final SchemaFactory<ReviewDb> schema) {
+ try {
+ final ReviewDb db = schema.open();
+ try {
+ final AccountSshKey k = db.accountSshKeys().get(id);
+ if (k != null) {
+ k.setLastUsedOn();
+ db.accountSshKeys().update(Collections.singleton(k));
+ }
+ } finally {
+ db.close();
+ }
+ } catch (OrmException e) {
+ log.warn("Failed to update \"" + id + "\" SSH key used", e);
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
new file mode 100644
index 0000000000..d51d6f9392
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshKeyCacheImpl.java
@@ -0,0 +1,156 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.common.errors.InvalidSshKeyException;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountSshKey;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.cache.Cache;
+import com.google.gerrit.server.cache.CacheModule;
+import com.google.gerrit.server.cache.SelfPopulatingCache;
+import com.google.gerrit.server.ssh.SshKeyCache;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Module;
+import com.google.inject.Singleton;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Provides the {@link SshKeyCacheEntry}. */
+@Singleton
+public class SshKeyCacheImpl implements SshKeyCache {
+ private static final Logger log =
+ LoggerFactory.getLogger(SshKeyCacheImpl.class);
+ private static final String CACHE_NAME = "sshkeys";
+
+ public static Module module() {
+ return new CacheModule() {
+ @Override
+ protected void configure() {
+ final TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>> type =
+ new TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>>() {};
+ core(type, CACHE_NAME);
+ bind(SshKeyCacheImpl.class);
+ bind(SshKeyCache.class).to(SshKeyCacheImpl.class);
+ }
+ };
+ }
+
+ private final SchemaFactory<ReviewDb> schema;
+ private final SelfPopulatingCache<String, Iterable<SshKeyCacheEntry>> self;
+
+ @Inject
+ SshKeyCacheImpl(final SchemaFactory<ReviewDb> schema,
+ @Named(CACHE_NAME) final Cache<String, Iterable<SshKeyCacheEntry>> raw) {
+ this.schema = schema;
+ self = new SelfPopulatingCache<String, Iterable<SshKeyCacheEntry>>(raw) {
+ @Override
+ protected Iterable<SshKeyCacheEntry> createEntry(final String username)
+ throws Exception {
+ return lookup(username);
+ }
+
+ @Override
+ protected Iterable<SshKeyCacheEntry> missing(final String username) {
+ return Collections.emptyList();
+ }
+ };
+ }
+
+ public Iterable<SshKeyCacheEntry> get(String username) {
+ return self.get(username);
+ }
+
+ public void evict(String username) {
+ self.remove(username);
+ }
+
+ @Override
+ public AccountSshKey create(AccountSshKey.Id id, String encoded)
+ throws InvalidSshKeyException {
+ try {
+ final AccountSshKey key =
+ new AccountSshKey(id, SshUtil.toOpenSshPublicKey(encoded));
+ SshUtil.parse(key);
+ return key;
+ } catch (NoSuchAlgorithmException e) {
+ throw new InvalidSshKeyException();
+
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidSshKeyException();
+
+ } catch (NoSuchProviderException e) {
+ log.error("Cannot parse SSH key", e);
+ throw new InvalidSshKeyException();
+ }
+ }
+
+ private Iterable<SshKeyCacheEntry> lookup(final String username)
+ throws Exception {
+ final ReviewDb db = schema.open();
+ try {
+ final Account user = db.accounts().bySshUserName(username);
+ if (user == null) {
+ return Collections.<SshKeyCacheEntry> emptyList();
+ }
+
+ final List<SshKeyCacheEntry> kl = new ArrayList<SshKeyCacheEntry>(4);
+ for (final AccountSshKey k : db.accountSshKeys().valid(user.getId())) {
+ add(db, kl, k);
+ }
+ if (kl.isEmpty()) {
+ return Collections.<SshKeyCacheEntry> emptyList();
+ }
+ return Collections.unmodifiableList(kl);
+ } finally {
+ db.close();
+ }
+ }
+
+ private void add(ReviewDb db, List<SshKeyCacheEntry> kl, AccountSshKey k) {
+ try {
+ kl.add(new SshKeyCacheEntry(k.getKey(), SshUtil.parse(k)));
+ } catch (OutOfMemoryError e) {
+ // This is the only case where we assume the problem has nothing
+ // to do with the key object, and instead we must abort this load.
+ //
+ throw e;
+ } catch (Throwable e) {
+ markInvalid(db, k);
+ }
+ }
+
+ private void markInvalid(final ReviewDb db, final AccountSshKey k) {
+ try {
+ log.info("Flagging SSH key " + k.getKey() + " invalid");
+ k.setInvalid();
+ db.accountSshKeys().update(Collections.singleton(k));
+ } catch (OrmException e) {
+ log.error("Failed to mark SSH key" + k.getKey() + " invalid", e);
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
new file mode 100644
index 0000000000..59d0c32eb3
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -0,0 +1,128 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import static com.google.inject.Scopes.SINGLETON;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.RemotePeer;
+import com.google.gerrit.server.config.FactoryModule;
+import com.google.gerrit.server.config.GerritRequestModule;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.ssh.SshInfo;
+import com.google.gerrit.sshd.args4j.AccountGroupIdHandler;
+import com.google.gerrit.sshd.args4j.AccountIdHandler;
+import com.google.gerrit.sshd.args4j.PatchSetIdHandler;
+import com.google.gerrit.sshd.args4j.ProjectControlHandler;
+import com.google.gerrit.sshd.commands.DefaultCommandModule;
+import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.gerrit.util.cli.OptionHandlerFactory;
+import com.google.gerrit.util.cli.OptionHandlerUtil;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryProvider;
+import com.google.inject.servlet.RequestScoped;
+import com.google.inject.servlet.SessionScoped;
+
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.session.ServerSession;
+import org.kohsuke.args4j.spi.OptionHandler;
+
+import java.net.SocketAddress;
+
+/** Configures standard dependencies for {@link SshDaemon}. */
+public class SshModule extends FactoryModule {
+ private static final String NAME = "Gerrit Code Review";
+
+ @Override
+ protected void configure() {
+ bindScope(SessionScoped.class, SshScopes.SESSION);
+ bindScope(RequestScoped.class, SshScopes.REQUEST);
+
+ configureSessionScope();
+ configureRequestScope();
+ configureCmdLineParser();
+
+ install(SshKeyCacheImpl.module());
+ bind(SshInfo.class).to(SshDaemon.class).in(SINGLETON);
+ factory(DispatchCommand.Factory.class);
+
+ bind(DispatchCommandProvider.class).annotatedWith(Commands.CMD_ROOT)
+ .toInstance(new DispatchCommandProvider(NAME, Commands.CMD_ROOT));
+ bind(CommandFactoryProvider.class);
+ bind(CommandFactory.class).toProvider(CommandFactoryProvider.class);
+
+ bind(PublickeyAuthenticator.class).to(DatabasePubKeyAuth.class);
+ bind(KeyPairProvider.class).toProvider(HostKeyProvider.class).in(SINGLETON);
+
+ install(new DefaultCommandModule());
+ }
+
+ private void configureSessionScope() {
+ bind(ServerSession.class).toProvider(new Provider<ServerSession>() {
+ @Override
+ public ServerSession get() {
+ return SshScopes.getContext().session;
+ }
+ }).in(SshScopes.SESSION);
+ bind(AbstractSession.class).to(ServerSession.class).in(SshScopes.SESSION);
+
+ bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
+ new Provider<SocketAddress>() {
+ @Override
+ public SocketAddress get() {
+ return SshScopes.getContext().session
+ .getAttribute(SshUtil.REMOTE_PEER);
+ }
+ }).in(SshScopes.SESSION);
+ }
+
+ private void configureRequestScope() {
+ install(new GerritRequestModule());
+ bind(IdentifiedUser.class).toProvider(SshCurrentUserProvider.class).in(
+ SshScopes.REQUEST);
+ bind(CurrentUser.class).to(IdentifiedUser.class);
+ }
+
+ private void configureCmdLineParser() {
+ factory(CmdLineParser.Factory.class);
+
+ registerOptionHandler(Account.Id.class, AccountIdHandler.class);
+ registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class);
+ registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
+ registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
+ }
+
+ private <T> void registerOptionHandler(Class<T> type,
+ Class<? extends OptionHandler<T>> impl) {
+ final Key<OptionHandlerFactory<T>> key = OptionHandlerUtil.keyFor(type);
+
+ final TypeLiteral<OptionHandlerFactory<T>> factoryType =
+ new TypeLiteral<OptionHandlerFactory<T>>() {};
+
+ final TypeLiteral<? extends OptionHandler<T>> implType =
+ TypeLiteral.get(impl);
+
+ bind(key).toProvider(FactoryProvider.newFactory(factoryType, implType));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScopes.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScopes.java
new file mode 100644
index 0000000000..ede05b1bef
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshScopes.java
@@ -0,0 +1,117 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.inject.Key;
+import com.google.inject.OutOfScopeException;
+import com.google.inject.Provider;
+import com.google.inject.Scope;
+
+import org.apache.sshd.common.session.AttributeKey;
+import org.apache.sshd.server.session.ServerSession;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Guice scopes for state during an SSH connection. */
+class SshScopes {
+ static class Context {
+ final ServerSession session;
+ final Map<Key<?>, Object> map;
+
+ Context(final ServerSession s) {
+ session = s;
+ map = new HashMap<Key<?>, Object>();
+ }
+ }
+
+ static final AttributeKey<Map<Key<?>, Object>> sessionMap =
+ new AttributeKey<Map<Key<?>, Object>>();
+
+ static final ThreadLocal<Context> current = new ThreadLocal<Context>();
+
+ static Context getContext() {
+ final Context ctx = current.get();
+ if (ctx == null) {
+ throw new OutOfScopeException(
+ "Cannot access scoped object; not in request/command.");
+ }
+ return ctx;
+ }
+
+ /** Returns exactly one instance for the scope of the SSH connection. */
+ static final Scope SESSION = new Scope() {
+ public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
+ return new Provider<T>() {
+ public T get() {
+ final Context ctx = getContext();
+ final Map<Key<?>, Object> map = ctx.session.getAttribute(sessionMap);
+ synchronized (map) {
+ @SuppressWarnings("unchecked")
+ T t = (T) map.get(key);
+ if (t == null) {
+ t = creator.get();
+ map.put(key, t);
+ }
+ return t;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[%s]", creator, SESSION);
+ }
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "SshScopes.SESSION";
+ }
+ };
+
+ /** Returns exactly one instance per command executed. */
+ static final Scope REQUEST = new Scope() {
+ public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
+ return new Provider<T>() {
+ public T get() {
+ final Map<Key<?>, Object> map = getContext().map;
+ synchronized (map) {
+ @SuppressWarnings("unchecked")
+ T t = (T) map.get(key);
+ if (t == null) {
+ t = creator.get();
+ map.put(key, t);
+ }
+ return t;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s[%s]", creator, REQUEST);
+ }
+ };
+ }
+
+ @Override
+ public String toString() {
+ return "SshScopes.REQUEST";
+ }
+ };
+
+ private SshScopes() {
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java
new file mode 100644
index 0000000000..df500a906d
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java
@@ -0,0 +1,135 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountSshKey;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.session.AttributeKey;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.server.CommandFactory.Command;
+import org.eclipse.jgit.lib.Constants;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.util.List;
+
+/** Utilities to support SSH operations. */
+public class SshUtil {
+ /** Server session attribute holding the {@link Account.Id}. */
+ public static final AttributeKey<Account.Id> CURRENT_ACCOUNT =
+ new AttributeKey<Account.Id>();
+
+ /** Server session attribute holding the remote {@link SocketAddress}. */
+ public static final AttributeKey<SocketAddress> REMOTE_PEER =
+ new AttributeKey<SocketAddress>();
+
+ /** Server session attribute holding the current commands. */
+ public static final AttributeKey<List<Command>> ACTIVE =
+ new AttributeKey<List<Command>>();
+
+ /**
+ * Parse a public key into its Java type.
+ *
+ * @param key the account key to parse.
+ * @return the valid public key object.
+ * @throws InvalidKeySpecException the key supplied is not a valid SSH key.
+ * @throws NoSuchAlgorithmException the JVM is missing the key algorithm.
+ * @throws NoSuchProviderException the JVM is missing the provider.
+ */
+ public static PublicKey parse(final AccountSshKey key)
+ throws NoSuchAlgorithmException, InvalidKeySpecException,
+ NoSuchProviderException {
+ try {
+ final String s = key.getEncodedKey();
+ if (s == null) {
+ throw new InvalidKeySpecException("No key string");
+ }
+ final byte[] bin = Base64.decodeBase64(Constants.encodeASCII(s));
+ return new Buffer(bin).getPublicKey();
+ } catch (RuntimeException re) {
+ throw new InvalidKeySpecException("Cannot parse key", re);
+ }
+ }
+
+ /**
+ * Convert an RFC 4716 style key to an OpenSSH style key.
+ *
+ * @param keyStr the key string to convert.
+ * @return <code>keyStr</code> if conversion failed; otherwise the converted
+ * key, in OpenSSH key format.
+ */
+ public static String toOpenSshPublicKey(final String keyStr) {
+ try {
+ final StringBuilder strBuf = new StringBuilder();
+ final BufferedReader br = new BufferedReader(new StringReader(keyStr));
+ String line = br.readLine(); // BEGIN SSH2 line...
+ if (!line.equals("---- BEGIN SSH2 PUBLIC KEY ----")) {
+ return keyStr;
+ }
+
+ while ((line = br.readLine()) != null) {
+ if (line.indexOf(':') == -1) {
+ strBuf.append(line);
+ break;
+ }
+ }
+
+ while ((line = br.readLine()) != null) {
+ if (line.startsWith("---- ")) {
+ break;
+ }
+ strBuf.append(line);
+ }
+
+ final PublicKey key =
+ new Buffer(Base64.decodeBase64(Constants.encodeASCII(strBuf
+ .toString()))).getPublicKey();
+ if (key instanceof RSAPublicKey) {
+ strBuf.insert(0, KeyPairProvider.SSH_RSA + " ");
+
+ } else if (key instanceof DSAPublicKey) {
+ strBuf.insert(0, KeyPairProvider.SSH_DSS + " ");
+
+ } else {
+ return keyStr;
+ }
+
+ strBuf.append(' ');
+ strBuf.append("converted-key");
+ return strBuf.toString();
+ } catch (IOException e) {
+ return keyStr;
+ } catch (RuntimeException re) {
+ return keyStr;
+ } catch (NoSuchAlgorithmException e) {
+ return keyStr;
+ } catch (InvalidKeySpecException e) {
+ return keyStr;
+ } catch (NoSuchProviderException e) {
+ return keyStr;
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java
new file mode 100644
index 0000000000..7966142b10
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupIdHandler.java
@@ -0,0 +1,57 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.args4j;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.server.account.GroupCache;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+public class AccountGroupIdHandler extends OptionHandler<AccountGroup.Id> {
+ private final GroupCache groupCache;
+
+ @SuppressWarnings("unchecked")
+ @Inject
+ public AccountGroupIdHandler(final GroupCache groupCache,
+ @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
+ @Assisted final Setter setter) {
+ super(parser, option, setter);
+ this.groupCache = groupCache;
+ }
+
+ @Override
+ public final int parseArguments(final Parameters params)
+ throws CmdLineException {
+ final String n = params.getParameter(0);
+ final AccountGroup group = groupCache.lookup(n);
+ if (group == null) {
+ throw new CmdLineException(owner, "Group \"" + n + "\" does not exist");
+ }
+ setter.addValue(group.getId());
+ return 1;
+ }
+
+ @Override
+ public final String getDefaultMetaVariable() {
+ return "GROUP";
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java
new file mode 100644
index 0000000000..fe74271e11
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountIdHandler.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.args4j;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+public class AccountIdHandler extends OptionHandler<Account.Id> {
+ private final AccountResolver accountResolver;
+
+ @SuppressWarnings("unchecked")
+ @Inject
+ public AccountIdHandler(final AccountResolver accountResolver,
+ @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
+ @Assisted final Setter setter) {
+ super(parser, option, setter);
+ this.accountResolver = accountResolver;
+ }
+
+ @Override
+ public final int parseArguments(final Parameters params)
+ throws CmdLineException {
+ final String token = params.getParameter(0);
+ final Account.Id accountId;
+ try {
+ final Account a = accountResolver.find(token);
+ if (a == null) {
+ throw new CmdLineException(owner, "\"" + token + "\" is not registered");
+ }
+ accountId = a.getId();
+ } catch (OrmException e) {
+ throw new CmdLineException(owner, "database is down");
+ }
+ setter.addValue(accountId);
+ return 1;
+ }
+
+ @Override
+ public final String getDefaultMetaVariable() {
+ return "EMAIL";
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java
new file mode 100644
index 0000000000..5b24ce5d46
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/PatchSetIdHandler.java
@@ -0,0 +1,56 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.args4j;
+
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+public class PatchSetIdHandler extends OptionHandler<PatchSet.Id> {
+ @SuppressWarnings("unchecked")
+ @Inject
+ public PatchSetIdHandler(@Assisted final CmdLineParser parser,
+ @Assisted final OptionDef option, @Assisted final Setter setter) {
+ super(parser, option, setter);
+ }
+
+ @Override
+ public final int parseArguments(final Parameters params)
+ throws CmdLineException {
+ final String token = params.getParameter(0);
+ final PatchSet.Id id;
+ try {
+ id = PatchSet.Id.parse(token);
+ } catch (IllegalArgumentException e) {
+ throw new CmdLineException(owner, "\"" + token
+ + "\" is not a valid patch set");
+ }
+
+ setter.addValue(id);
+ return 1;
+ }
+
+ @Override
+ public final String getDefaultMetaVariable() {
+ return "CHANGE,PATCHSET";
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java
new file mode 100644
index 0000000000..4c962f04da
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/ProjectControlHandler.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.args4j;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+public class ProjectControlHandler extends OptionHandler<ProjectControl> {
+ private final ProjectControl.Factory projectControlFactory;
+
+ @SuppressWarnings("unchecked")
+ @Inject
+ public ProjectControlHandler(
+ final ProjectControl.Factory projectControlFactory,
+ @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
+ @Assisted final Setter setter) {
+ super(parser, option, setter);
+ this.projectControlFactory = projectControlFactory;
+ }
+
+ @Override
+ public final int parseArguments(final Parameters params)
+ throws CmdLineException {
+ final String token = params.getParameter(0);
+ String projectName = token;
+
+ if (projectName.endsWith(".git")) {
+ // Be nice and drop the trailing ".git" suffix, which we never keep
+ // in our database, but clients might mistakenly provide anyway.
+ //
+ projectName = projectName.substring(0, projectName.length() - 4);
+ }
+
+ if (projectName.startsWith("/")) {
+ // Be nice and drop the leading "/" if supplied by an absolute path.
+ // We don't have a file system hierarchy, just a flat namespace in
+ // the database's Project entities. We never encode these with a
+ // leading '/' but users might accidentally include them in Git URLs.
+ //
+ projectName = projectName.substring(1);
+ }
+
+ final ProjectControl control;
+ try {
+ control =
+ projectControlFactory.validateFor(new Project.NameKey(projectName));
+ } catch (NoSuchProjectException e) {
+ throw new CmdLineException(owner, "'" + token + "': not a Gerrit project");
+ }
+
+ setter.addValue(control);
+ return 1;
+ }
+
+ @Override
+ public final String getDefaultMetaVariable() {
+ return "PROJECT";
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AbstractGitCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AbstractGitCommand.java
new file mode 100644
index 0000000000..8193c61b22
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AbstractGitCommand.java
@@ -0,0 +1,68 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.inject.Inject;
+
+import org.kohsuke.args4j.Argument;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.IOException;
+
+abstract class AbstractGitCommand extends BaseCommand {
+ @Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
+ protected ProjectControl projectControl;
+
+ @Inject
+ protected GitRepositoryManager repoManager;
+
+ protected Repository repo;
+ protected Project project;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ AbstractGitCommand.this.service();
+ }
+ });
+ }
+
+ private void service() throws IOException, Failure {
+ project = projectControl.getProjectState().getProject();
+
+ final String name = project.getName();
+ try {
+ repo = repoManager.openRepository(name);
+ } catch (RepositoryNotFoundException e) {
+ throw new Failure(1, "fatal: '" + name + "': not a git archive", e);
+ }
+
+ try {
+ runImpl();
+ } finally {
+ repo.close();
+ }
+ }
+
+ protected abstract void runImpl() throws IOException, Failure;
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java
new file mode 100644
index 0000000000..b8e1ccc8e0
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminCreateProject.java
@@ -0,0 +1,150 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ProjectRight;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.reviewdb.Project.SubmitType;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.sshd.AdminCommand;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.kohsuke.args4j.Option;
+
+import java.io.PrintWriter;
+import java.util.Collections;
+
+/** Create a new project. **/
+@AdminCommand
+final class AdminCreateProject extends BaseCommand {
+ @Option(name = "--name", required = true, aliases = {"-n"}, metaVar = "NAME", usage = "name of project to be created")
+ private String projectName;
+
+ @Option(name = "--owner", aliases = {"-o"}, usage = "owner of project\n"
+ + "(default: Administrators)")
+ private AccountGroup.Id ownerId;
+
+ @Option(name = "--description", aliases = {"-d"}, metaVar = "DESC", usage = "description of project")
+ private String projectDescription = "";
+
+ @Option(name = "--submit-type", aliases = {"-t"}, usage = "project submit type\n"
+ + "(default: MERGE_IF_NECESSARY)")
+ private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY;
+
+ @Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
+ private boolean contributorAgreements;
+
+ @Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
+ private boolean signedOffBy;
+
+ @Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
+ + "(default: master)")
+ private String branch = Constants.MASTER;
+
+ @Inject
+ private ReviewDb db;
+
+ @Inject
+ private GitRepositoryManager repoManager;
+
+ @Inject
+ private AuthConfig authConfig;
+
+ @Inject
+ private ReplicationQueue rq;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ PrintWriter p = toPrintWriter(out);
+
+ ownerId = authConfig.getAdministratorsGroup();
+ parseCommandLine();
+
+ try {
+ validateParameters();
+
+ Transaction txn = db.beginTransaction();
+
+ createProject(txn);
+
+ Repository repo = repoManager.createRepository(projectName);
+ repo.create(true);
+ repo.writeSymref(Constants.HEAD, branch);
+ repoManager.setProjectDescription(projectName, projectDescription);
+
+ txn.commit();
+
+ rq.replicateNewProject(new Project.NameKey(projectName), branch);
+ } catch (Exception e) {
+ p.print("Error when trying to create project: " + e.getMessage()
+ + "\n");
+ p.flush();
+ }
+
+ }
+ });
+ }
+
+ private void createProject(Transaction txn) throws OrmException {
+ final Project.NameKey newProjectNameKey = new Project.NameKey(projectName);
+
+ final Project newProject =
+ new Project(newProjectNameKey, new Project.Id(db.nextProjectId()));
+
+ newProject.setDescription(projectDescription);
+ newProject.setSubmitType(submitType);
+ newProject.setUseContributorAgreements(contributorAgreements);
+ newProject.setUseSignedOffBy(signedOffBy);
+
+ db.projects().insert(Collections.singleton(newProject), txn);
+
+ final ProjectRight.Key prk =
+ new ProjectRight.Key(newProjectNameKey, ApprovalCategory.OWN, ownerId);
+ final ProjectRight pr = new ProjectRight(prk);
+ pr.setMaxValue((short) 1);
+ pr.setMinValue((short) 1);
+ db.projectRights().insert(Collections.singleton(pr), txn);
+ }
+
+ private void validateParameters() throws Failure {
+ if (projectName.endsWith(".git")) {
+ projectName =
+ projectName.substring(0, projectName.length() - ".git".length());
+ }
+
+ while (branch.startsWith("/")) {
+ branch = branch.substring(1);
+ }
+ if (!branch.startsWith(Constants.R_HEADS)) {
+ branch = Constants.R_HEADS + branch;
+ }
+ if (!Repository.isValidRefName(branch)) {
+ throw new Failure(1, "--branch \"" + branch + "\" is not a valid name");
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminFlushCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminFlushCaches.java
new file mode 100644
index 0000000000..8f44595418
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminFlushCaches.java
@@ -0,0 +1,124 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.AdminCommand;
+
+import net.sf.ehcache.Ehcache;
+
+import org.kohsuke.args4j.Option;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+
+/** Causes the caches to purge all entries and reload. */
+@AdminCommand
+final class AdminFlushCaches extends CacheCommand {
+ @Option(name = "--cache", usage = "flush named cache", metaVar = "NAME")
+ private List<String> caches = new ArrayList<String>();
+
+ @Option(name = "--all", usage = "flush all caches")
+ private boolean all;
+
+ @Option(name = "--list", usage = "list available caches")
+ private boolean list;
+
+ private PrintWriter p;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ flush();
+ }
+ });
+ }
+
+ private void flush() throws Failure {
+ p = toPrintWriter(err);
+ if (list) {
+ if (all || caches.size() > 0) {
+ throw error("error: cannot use --list with --all or --cache");
+ }
+ doList();
+ return;
+ }
+
+ if (all && caches.size() > 0) {
+ throw error("error: cannot combine --all and --cache");
+ } else if (!all && caches.size() == 1 && caches.contains("all")) {
+ caches.clear();
+ all = true;
+ } else if (!all && caches.isEmpty()) {
+ all = true;
+ }
+
+ final SortedSet<String> names = cacheNames();
+ for (final String n : caches) {
+ if (!names.contains(n)) {
+ throw error("error: cache \"" + n + "\" not recognized");
+ }
+ }
+ doBulkFlush();
+ }
+
+ private static UnloggedFailure error(final String msg) {
+ return new UnloggedFailure(1, msg);
+ }
+
+ private void doList() {
+ for (final String name : cacheNames()) {
+ p.print(name);
+ p.print('\n');
+ }
+ p.flush();
+ }
+
+ private void doBulkFlush() {
+ try {
+ for (final Ehcache c : getAllCaches()) {
+ final String name = c.getName();
+ if (flush(name)) {
+ try {
+ c.removeAll();
+ } catch (Throwable e) {
+ p.println("error: cannot flush cache \"" + name + "\": " + e);
+ }
+ }
+ }
+ } finally {
+ p.flush();
+ }
+ }
+
+ private boolean flush(final String cacheName) {
+ if (caches.contains(cacheName)) {
+ return true;
+
+ } else if (all) {
+ if ("web_sessions".equals(cacheName)) {
+ return false;
+ }
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminReplicate.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminReplicate.java
new file mode 100644
index 0000000000..3f131c6e2c
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminReplicate.java
@@ -0,0 +1,87 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.server.git.PushAllProjectsOp;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.sshd.AdminCommand;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.inject.Inject;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/** Force a project to replicate, again. */
+@AdminCommand
+final class AdminReplicate extends BaseCommand {
+ @Option(name = "--all", usage = "push all known projects")
+ private boolean all;
+
+ @Option(name = "--url", metaVar = "PATTERN", usage = "pattern to match URL on")
+ private String urlMatch;
+
+ @Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project name")
+ private List<String> projectNames = new ArrayList<String>(2);
+
+ @Inject
+ private PushAllProjectsOp.Factory pushAllOpFactory;
+
+ @Inject
+ private ReplicationQueue replication;
+
+ @Inject
+ private ProjectCache projectCache;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ AdminReplicate.this.schedule();
+ }
+ });
+ }
+
+ private void schedule() throws Failure {
+ if (all && projectNames.size() > 0) {
+ throw new Failure(1, "error: cannot combine --all and PROJECT");
+ }
+
+ if (!replication.isEnabled()) {
+ throw new Failure(1, "error: replication not enabled");
+ }
+
+ if (all) {
+ pushAllOpFactory.create(urlMatch).start(0, TimeUnit.SECONDS);
+
+ } else {
+ for (final String name : projectNames) {
+ final Project.NameKey key = new Project.NameKey(name);
+ if (projectCache.get(key) != null) {
+ replication.scheduleFullSync(key, urlMatch);
+ } else {
+ throw new Failure(1, "error: '" + name + "': not a Gerrit project");
+ }
+ }
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowCaches.java
new file mode 100644
index 0000000000..c865f7d7d7
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowCaches.java
@@ -0,0 +1,207 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.AdminCommand;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Statistics;
+import net.sf.ehcache.config.CacheConfiguration;
+
+import org.eclipse.jgit.lib.WindowCacheStatAccessor;
+
+import java.io.PrintWriter;
+
+/** Show the current cache states. */
+@AdminCommand
+final class AdminShowCaches extends CacheCommand {
+ private PrintWriter p;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ display();
+ }
+ });
+ }
+
+ private void display() {
+ p = toPrintWriter(out);
+
+ p.print(String.format(//
+ "%1s %-18s %-4s|%-20s| %-5s |%-14s|\n" //
+ , "" //
+ , "Name" //
+ , "Max" //
+ , "Object Count" //
+ , "AvgGet" //
+ , "Hit Ratio" //
+ ));
+ p.print(String.format(//
+ "%1s %-18s %-4s|%6s %6s %6s| %-5s |%-4s %-4s %-4s|\n" //
+ , "" //
+ , "" //
+ , "Age" //
+ , "Disk" //
+ , "Mem" //
+ , "Cnt" //
+ , "" //
+ , "Disk" //
+ , "Mem" //
+ , "Agg" //
+ ));
+ p.println("------------------"
+ + "-------+--------------------+----------+--------------+");
+ for (final Ehcache cache : getAllCaches()) {
+ final CacheConfiguration cfg = cache.getCacheConfiguration();
+ final boolean useDisk = cfg.isDiskPersistent() || cfg.isOverflowToDisk();
+ final Statistics stat = cache.getStatistics();
+ final long total = stat.getCacheHits() + stat.getCacheMisses();
+
+ if (useDisk) {
+ p.print(String.format(//
+ "D %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
+ , cache.getName() //
+ , interval(cfg.getTimeToLiveSeconds()) //
+ , count(stat.getDiskStoreObjectCount()) //
+ , count(stat.getMemoryStoreObjectCount()) //
+ , count(stat.getObjectCount()) //
+ , duration(stat.getAverageGetTime()) //
+ , percent(stat.getOnDiskHits(), total) //
+ , percent(stat.getInMemoryHits(), total) //
+ , percent(stat.getCacheHits(), total) //
+ ));
+ } else {
+ p.print(String.format(//
+ " %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
+ , cache.getName() //
+ , interval(cfg.getTimeToLiveSeconds()) //
+ , "", "" //
+ , count(stat.getObjectCount()) //
+ , duration(stat.getAverageGetTime()) //
+ , "", "" //
+ , percent(stat.getCacheHits(), total) //
+ ));
+ }
+ }
+ p.println();
+
+ final Runtime r = Runtime.getRuntime();
+ final long mMax = r.maxMemory();
+ final long mFree = r.freeMemory();
+ final long mTotal = r.totalMemory();
+ final long mInuse = mTotal - mFree;
+ final long jgitBytes = WindowCacheStatAccessor.getOpenBytes();
+
+ p.println("JGit Buffer Cache:");
+ fItemCount("open files", WindowCacheStatAccessor.getOpenFiles());
+ fByteCount("loaded", jgitBytes);
+ fPercent("mem%", jgitBytes, mTotal);
+ p.println();
+
+ p.println("JVM Heap:");
+ fByteCount("max", mMax);
+ fByteCount("inuse", mInuse);
+ fPercent("mem%", mInuse, mTotal);
+ p.println();
+
+ p.flush();
+ }
+
+ private void fItemCount(final String name, final long value) {
+ p.println(String.format(" %1$-12s: %2$15d", name, value));
+ }
+
+ private void fByteCount(final String name, double value) {
+ String suffix = "bytes";
+ if (value > 1024) {
+ value /= 1024;
+ suffix = "kb";
+ }
+ if (value > 1024) {
+ value /= 1024;
+ suffix = "mb";
+ }
+ if (value > 1024) {
+ value /= 1024;
+ suffix = "gb";
+ }
+ p.println(String.format(" %1$-12s: %2$6.2f %3$s", name, value, suffix));
+ }
+
+ private String count(long cnt) {
+ if (cnt == 0) {
+ return "";
+ }
+ return String.format("%6d", cnt);
+ }
+
+ private String duration(double ms) {
+ if (Math.abs(ms) <= 0.05) {
+ return "";
+ }
+ String suffix = "ms";
+ if (ms >= 1000) {
+ ms /= 1000;
+ suffix = "s ";
+ }
+ return String.format("%4.1f%s", ms, suffix);
+ }
+
+ private String interval(double ttl) {
+ if (ttl == 0) {
+ return "inf";
+ }
+
+ String suffix = "s";
+ if (ttl >= 60) {
+ ttl /= 60;
+ suffix = "m";
+
+ if (ttl >= 60) {
+ ttl /= 60;
+ suffix = "h";
+ }
+
+ if (ttl >= 24) {
+ ttl /= 24;
+ suffix = "d";
+
+ if (ttl >= 365) {
+ ttl /= 365;
+ suffix = "y";
+ }
+ }
+ }
+
+ return Integer.toString((int) ttl) + suffix;
+ }
+
+ private String percent(final long value, final long total) {
+ if (total <= 0) {
+ return "";
+ }
+ final long pcent = (100 * value) / total;
+ return String.format("%3d%%", (int) pcent);
+ }
+
+ private void fPercent(final String name, final long value, final long total) {
+ final long pcent = 0 < total ? (100 * value) / total : 0;
+ p.println(String.format(" %1$-12s: %2$3d%%", name, (int) pcent));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowConnections.java
new file mode 100644
index 0000000000..ff19e4bcc7
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowConnections.java
@@ -0,0 +1,167 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.sshd.AdminCommand;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.gerrit.sshd.SshDaemon;
+import com.google.gerrit.sshd.SshUtil;
+import com.google.inject.Inject;
+
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.session.IoSession;
+import org.apache.sshd.server.CommandFactory.Command;
+import org.apache.sshd.server.session.ServerSession;
+import org.kohsuke.args4j.Option;
+
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+/** Show the current SSH connections. */
+@AdminCommand
+final class AdminShowConnections extends BaseCommand {
+ @Option(name = "--numeric", aliases = {"-n"}, usage = "don't resolve names")
+ private boolean numeric;
+
+ private PrintWriter p;
+
+ @Inject
+ private SshDaemon daemon;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ AdminShowConnections.this.display();
+ }
+ });
+ }
+
+ private void display() throws Failure {
+ p = toPrintWriter(out);
+
+ final IoAcceptor acceptor = daemon.getIoAcceptor();
+ if (acceptor == null) {
+ throw new Failure(1, "fatal: sshd no longer running");
+ }
+
+ final List<IoSession> list =
+ new ArrayList<IoSession>(acceptor.getManagedSessions().values());
+ Collections.sort(list, new Comparator<IoSession>() {
+ @Override
+ public int compare(IoSession arg0, IoSession arg1) {
+ if (arg0.getCreationTime() < arg1.getCreationTime()) {
+ return -1;
+ } else if (arg0.getCreationTime() > arg1.getCreationTime()) {
+ return 1;
+ }
+ return (int) (arg0.getId() - arg1.getId());
+ }
+ });
+
+ final long now = System.currentTimeMillis();
+ p.print(String.format(" %8s %8s %-15s %s\n", "Start", "Idle", "User",
+ "Remote Host"));
+ p.print("--------------------------------------------------------------\n");
+ for (final IoSession io : list) {
+ ServerSession s = (ServerSession) ServerSession.getSession(io, true);
+ List<Command> active = s != null ? s.getAttribute(SshUtil.ACTIVE) : null;
+
+ final SocketAddress remoteAddress = io.getRemoteAddress();
+ final long start = io.getCreationTime();
+ final long idle = now - io.getLastIoTime();
+
+ p.print(String.format(" %8s %8s %-15.15s %.30s\n", time(now, start),
+ age(idle), username(s), hostname(remoteAddress)));
+ if (active != null) {
+ synchronized (active) {
+ for (final Command cmd : active) {
+ p.print(String.format(" [ %s ]\n", cmd.toString()));
+ }
+ }
+ }
+ p.print("\n");
+ }
+ p.print("--\n");
+
+ p.flush();
+ }
+
+ private static String time(final long now, final long time) {
+ if (time - now < 24 * 60 * 60 * 1000L) {
+ return new SimpleDateFormat("HH:mm:ss").format(new Date(time));
+ }
+ return new SimpleDateFormat("MMM-dd").format(new Date(time));
+ }
+
+ private static String age(long age) {
+ age /= 1000;
+
+ final int sec = (int) (age % 60);
+ age /= 60;
+
+ final int min = (int) (age % 60);
+ age /= 60;
+
+ final int hr = (int) (age % 60);
+ return String.format("%02d:%02d:%02d", hr, min, sec);
+ }
+
+ private String username(final ServerSession s) {
+ if (s == null) {
+ return "";
+ } else if (numeric) {
+ final Account.Id id = s.getAttribute(SshUtil.CURRENT_ACCOUNT);
+ return id != null ? "a/" + id.toString() : "";
+ } else {
+ final String user = s.getUsername();
+ return user != null ? user : "";
+ }
+ }
+
+ private String hostname(final SocketAddress remoteAddress) {
+ if (remoteAddress == null) {
+ return "?";
+ }
+ String host = null;
+ if (remoteAddress instanceof InetSocketAddress) {
+ final InetSocketAddress sa = (InetSocketAddress) remoteAddress;
+ final InetAddress in = sa.getAddress();
+ if (numeric) {
+ return in.getHostAddress();
+ }
+ if (in != null) {
+ host = in.getCanonicalHostName();
+ } else {
+ host = sa.getHostName();
+ }
+ }
+ if (host == null) {
+ host = remoteAddress.toString();
+ }
+ return host;
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowQueue.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowQueue.java
new file mode 100644
index 0000000000..5f8741749f
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminShowQueue.java
@@ -0,0 +1,133 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.git.WorkQueue.Task;
+import com.google.gerrit.sshd.AdminCommand;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.inject.Inject;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/** Display the current work queue. */
+@AdminCommand
+final class AdminShowQueue extends BaseCommand {
+ @Inject
+ private WorkQueue workQueue;
+
+ private PrintWriter p;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ AdminShowQueue.this.display();
+ }
+ });
+ }
+
+ private void display() {
+ p = toPrintWriter(out);
+
+ final List<Task<?>> pending = workQueue.getTasks();
+ Collections.sort(pending, new Comparator<Task<?>>() {
+ public int compare(Task<?> a, Task<?> b) {
+ final Task.State aState = a.getState();
+ final Task.State bState = b.getState();
+
+ if (aState != bState) {
+ return aState.ordinal() - bState.ordinal();
+ }
+
+ final long aDelay = a.getDelay(TimeUnit.MILLISECONDS);
+ final long bDelay = b.getDelay(TimeUnit.MILLISECONDS);
+
+ if (aDelay < bDelay) {
+ return -1;
+ } else if (aDelay > bDelay) {
+ return 1;
+ }
+ return format(a).compareTo(format(b));
+ }
+ });
+
+ p.print(String.format(" %1s %-12s %s\n", "S", "Start", "Task"));
+ p.print("--------------------------------------------------------------\n");
+
+ final long now = System.currentTimeMillis();
+ for (final Task<?> task : pending) {
+ final long delay = task.getDelay(TimeUnit.MILLISECONDS);
+ final Task.State state = task.getState();
+
+ final String start;
+ switch (state) {
+ case DONE:
+ case CANCELLED:
+ case RUNNING:
+ case READY:
+ start = "";
+ break;
+ default:
+ start = time(now, delay);
+ break;
+ }
+
+ p.print(String.format(" %1s %12s %s\n", format(state), start,
+ format(task)));
+ }
+ p.print("--------------------------------------------------------------\n");
+ p.print(" " + pending.size() + " tasks\n");
+
+ p.flush();
+ }
+
+ private static String time(final long now, final long delay) {
+ final Date when = new Date(now + delay);
+ if (delay < 24 * 60 * 60 * 1000L) {
+ return new SimpleDateFormat("HH:mm:ss.SSS").format(when);
+ }
+ return new SimpleDateFormat("MMM-dd HH:mm").format(when);
+ }
+
+ private static String format(final Task<?> task) {
+ return task.getRunnable().toString();
+ }
+
+ private static String format(final Task.State state) {
+ switch (state) {
+ case DONE:
+ return "D";
+ case CANCELLED:
+ return "C";
+ case RUNNING:
+ return "R";
+ case READY:
+ return "W";
+ case SLEEPING:
+ return "S";
+ default:
+ return " ";
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveCommand.java
new file mode 100644
index 0000000000..e8fca51567
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveCommand.java
@@ -0,0 +1,337 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.RevId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.mail.CommentSender;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.workflow.FunctionState;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.gerrit.util.cli.CmdLineParser;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.ResultSet;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ApproveCommand extends BaseCommand {
+ private static final Logger log =
+ LoggerFactory.getLogger(ApproveCommand.class);
+
+ @Override
+ protected final CmdLineParser newCmdLineParser() {
+ final CmdLineParser parser = super.newCmdLineParser();
+ for (ApproveOption c : optionList) {
+ parser.addOption(c, c);
+ }
+ return parser;
+ }
+
+ private final Set<PatchSet.Id> patchSetIds = new HashSet<PatchSet.Id>();
+
+ @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "patch to approve")
+ void addPatchSetId(final String token) {
+ try {
+ patchSetIds.addAll(parsePatchSetId(token));
+ } catch (UnloggedFailure e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ } catch (OrmException e) {
+ throw new IllegalArgumentException("database error", e);
+ }
+ }
+
+ @Option(name = "--project", aliases = "-p", usage = "project containing the patch set")
+ private ProjectControl projectControl;
+
+ @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change", metaVar = "MESSAGE")
+ private String changeComment;
+
+ @Inject
+ private ReviewDb db;
+
+ @Inject
+ private IdentifiedUser currentUser;
+
+ @Inject
+ private CommentSender.Factory commentSenderFactory;
+
+ @Inject
+ private PatchSetInfoFactory patchSetInfoFactory;
+
+ @Inject
+ private ApprovalTypes approvalTypes;
+
+ @Inject
+ private ChangeControl.Factory changeControlFactory;
+
+ @Inject
+ private FunctionState.Factory functionStateFactory;
+
+ private List<ApproveOption> optionList;
+
+ @Override
+ public final void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Failure {
+ initOptionList();
+ parseCommandLine();
+
+ boolean ok = true;
+ for (final PatchSet.Id patchSetId : patchSetIds) {
+ try {
+ approveOne(patchSetId);
+ } catch (UnloggedFailure e) {
+ ok = false;
+ writeError("error: " + e.getMessage() + "\n");
+ } catch (Exception e) {
+ ok = false;
+ writeError("fatal: internal server error while approving "
+ + patchSetId + "\n");
+ log.error("internal error while approving " + patchSetId);
+ }
+ }
+ if (!ok) {
+ throw new UnloggedFailure(1, "one or more approvals failed;"
+ + " review output above");
+ }
+ }
+ });
+ }
+
+ private void approveOne(final PatchSet.Id patchSetId)
+ throws NoSuchChangeException, UnloggedFailure, OrmException,
+ PatchSetInfoNotAvailableException, EmailException {
+ final Change.Id changeId = patchSetId.getParentKey();
+ final ChangeControl changeControl =
+ changeControlFactory.validateFor(changeId);
+ final Change change = changeControl.getChange();
+ if (change.getStatus().isClosed()) {
+ throw error("change " + changeId + " is closed");
+ }
+
+ final Transaction txn = db.beginTransaction();
+ final StringBuffer msgBuf = new StringBuffer();
+ msgBuf.append("Patch Set ");
+ msgBuf.append(patchSetId.get());
+ msgBuf.append(": ");
+
+ for (ApproveOption co : optionList) {
+ final ApprovalCategory.Id category = co.getCategoryId();
+ PatchSetApproval.Key psaKey =
+ new PatchSetApproval.Key(patchSetId, currentUser.getAccountId(),
+ category);
+ PatchSetApproval psa = db.patchSetApprovals().get(psaKey);
+
+ Short score = co.value();
+
+ if (score != null) {
+ addApproval(psaKey, score, change, co, txn);
+ } else {
+ if (psa == null) {
+ score = 0;
+ addApproval(psaKey, score, change, co, txn);
+ } else {
+ score = psa.getValue();
+ }
+ }
+
+ String message =
+ db.approvalCategoryValues().get(
+ new ApprovalCategoryValue.Id(category, score)).getName();
+ msgBuf.append(" " + message + ";");
+ }
+
+ msgBuf.deleteCharAt(msgBuf.length() - 1);
+ msgBuf.append("\n\n");
+
+ if (changeComment != null) {
+ msgBuf.append(changeComment);
+ }
+
+ String uuid = ChangeUtil.messageUUID(db);
+ ChangeMessage cm =
+ new ChangeMessage(new ChangeMessage.Key(changeId, uuid), currentUser
+ .getAccountId());
+ cm.setMessage(msgBuf.toString());
+ db.changeMessages().insert(Collections.singleton(cm), txn);
+ ChangeUtil.updated(change);
+ db.changes().update(Collections.singleton(change), txn);
+ txn.commit();
+ sendMail(change, change.currentPatchSetId(), cm);
+ }
+
+ private Set<PatchSet.Id> parsePatchSetId(final String patchIdentity)
+ throws UnloggedFailure, OrmException {
+ // By commit?
+ //
+ if (patchIdentity.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
+ final RevId id = new RevId(patchIdentity);
+ final ResultSet<PatchSet> patches;
+ if (id.isComplete()) {
+ patches = db.patchSets().byRevision(id);
+ } else {
+ patches = db.patchSets().byRevisionRange(id, id.max());
+ }
+
+ final Set<PatchSet.Id> matches = new HashSet<PatchSet.Id>();
+ for (final PatchSet ps : patches) {
+ final Change change = db.changes().get(ps.getId().getParentKey());
+ if (inProject(change)) {
+ matches.add(ps.getId());
+ }
+ }
+
+ switch (matches.size()) {
+ case 1:
+ return matches;
+ case 0:
+ throw error("\"" + patchIdentity + "\" no such patch set");
+ default:
+ throw error("\"" + patchIdentity + "\" matches multiple patch sets");
+ }
+ }
+
+ // By older style change,patchset?
+ //
+ if (patchIdentity.matches("^[1-9][0-9]*,[1-9][0-9]*$")) {
+ final PatchSet.Id patchSetId;
+ try {
+ patchSetId = PatchSet.Id.parse(patchIdentity);
+ } catch (IllegalArgumentException e) {
+ throw error("\"" + patchIdentity + "\" is not a valid patch set");
+ }
+ if (db.patchSets().get(patchSetId) == null) {
+ throw error("\"" + patchIdentity + "\" no such patch set");
+ }
+ if (projectControl != null) {
+ final Change change = db.changes().get(patchSetId.getParentKey());
+ if (!inProject(change)) {
+ throw error("change " + change.getId() + " not in project "
+ + projectControl.getProject().getName());
+ }
+ }
+ return Collections.singleton(patchSetId);
+ }
+
+ throw error("\"" + patchIdentity + "\" is not a valid patch set");
+ }
+
+ private boolean inProject(final Change change) {
+ if (projectControl == null) {
+ // No --project option, so they want every project.
+ return true;
+ }
+ return projectControl.getProject().getNameKey().equals(change.getProject());
+ }
+
+ private void sendMail(final Change c, final PatchSet.Id psid,
+ final ChangeMessage message) throws PatchSetInfoNotAvailableException,
+ EmailException, OrmException {
+ PatchSet ps = db.patchSets().get(psid);
+ final CommentSender cm;
+ cm = commentSenderFactory.create(c);
+ cm.setFrom(currentUser.getAccountId());
+ cm.setPatchSet(ps, patchSetInfoFactory.get(psid));
+ cm.setChangeMessage(message);
+ cm.setReviewDb(db);
+ cm.send();
+ }
+
+ private void addApproval(final PatchSetApproval.Key psaKey,
+ final Short score, final Change c, final ApproveOption co,
+ final Transaction txn) throws OrmException, UnloggedFailure {
+ PatchSetApproval psa = db.patchSetApprovals().get(psaKey);
+ boolean insert = false;
+
+ if (psa == null) {
+ insert = true;
+ psa = new PatchSetApproval(psaKey, score);
+ }
+
+ final List<PatchSetApproval> approvals = Collections.emptyList();
+ final FunctionState fs =
+ functionStateFactory.create(c, psaKey.getParentKey(), approvals);
+ psa.setValue(score);
+ fs.normalize(approvalTypes.getApprovalType(psa.getCategoryId()), psa);
+ if (score != psa.getValue()) {
+ throw error(co.name() + "=" + co.value() + " not permitted");
+ }
+
+ psa.setGranted();
+
+ if (insert) {
+ db.patchSetApprovals().insert(Collections.singleton(psa), txn);
+ } else {
+ db.patchSetApprovals().update(Collections.singleton(psa), txn);
+ }
+ }
+
+ private void initOptionList() {
+ optionList = new ArrayList<ApproveOption>();
+
+ for (ApprovalType type : approvalTypes.getApprovalTypes()) {
+ String usage = "";
+ final ApprovalCategory category = type.getCategory();
+ usage = "score for " + category.getName() + "\n";
+
+ for (ApprovalCategoryValue v : type.getValues()) {
+ usage += v.format() + "\n";
+ }
+
+ final String name =
+ "--" + category.getName().toLowerCase().replace(' ', '-');
+ optionList.add(new ApproveOption(name, usage, type));
+ }
+ }
+
+ private void writeError(final String msg) {
+ try {
+ err.write(msg.getBytes(ENC));
+ } catch (IOException e) {
+ }
+ }
+
+ private static UnloggedFailure error(final String msg) {
+ return new UnloggedFailure(1, msg);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java
new file mode 100644
index 0000000000..cc23d1d254
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ApproveOption.java
@@ -0,0 +1,138 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.ApprovalCategoryValue;
+
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+import java.lang.annotation.Annotation;
+
+final class ApproveOption implements Option, Setter<Short> {
+ private final String name;
+ private final String usage;
+ private final ApprovalType type;
+
+ private Short value;
+
+ ApproveOption(final String name, final String usage, final ApprovalType type) {
+ this.name = name;
+ this.usage = usage;
+ this.type = type;
+ }
+
+ @Override
+ public String[] aliases() {
+ return new String[0];
+ }
+
+ @Override
+ public Class<? extends OptionHandler<Short>> handler() {
+ return Handler.class;
+ }
+
+ @Override
+ public String metaVar() {
+ return "N";
+ }
+
+ @Override
+ public boolean multiValued() {
+ return false;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public boolean required() {
+ return false;
+ }
+
+ @Override
+ public String usage() {
+ return usage;
+ }
+
+ public Short value() {
+ return value;
+ }
+
+ @Override
+ public Class<? extends Annotation> annotationType() {
+ return null;
+ }
+
+ @Override
+ public void addValue(final Short val) {
+ this.value = val;
+ }
+
+ @Override
+ public Class<Short> getType() {
+ return Short.class;
+ }
+
+ @Override
+ public boolean isMultiValued() {
+ return false;
+ }
+
+ ApprovalCategory.Id getCategoryId() {
+ return type.getCategory().getId();
+ }
+
+ public static class Handler extends OneArgumentOptionHandler<Short> {
+ private final ApproveOption cmdOption;
+
+ public Handler(final CmdLineParser parser, final OptionDef option,
+ final Setter<Short> setter) {
+ super(parser, option, setter);
+ this.cmdOption = (ApproveOption) setter;
+ }
+
+ @Override
+ protected Short parse(final String token) throws NumberFormatException,
+ CmdLineException {
+ String argument = token;
+ if (argument.startsWith("+")) {
+ argument = argument.substring(1);
+ }
+
+ final short value = Short.parseShort(argument);
+ final ApprovalCategoryValue min = cmdOption.type.getMin();
+ final ApprovalCategoryValue max = cmdOption.type.getMax();
+
+ if (value < min.getValue() || value > max.getValue()) {
+ final String name = cmdOption.name();
+ final String e =
+ "\"" + token + "\" must be in range " + min.formatValue() + ".."
+ + max.formatValue() + " for \"" + name + "\"";
+ throw new CmdLineException(owner, e);
+ }
+ return value;
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
new file mode 100644
index 0000000000..313b9dc3df
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CacheCommand.java
@@ -0,0 +1,50 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.inject.Inject;
+
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Ehcache;
+
+import java.util.Arrays;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+abstract class CacheCommand extends BaseCommand {
+ @Inject
+ protected CachePool cachePool;
+
+ protected SortedSet<String> cacheNames() {
+ final SortedSet<String> names = new TreeSet<String>();
+ for (final Ehcache c : getAllCaches()) {
+ names.add(c.getName());
+ }
+ return names;
+ }
+
+ protected Ehcache[] getAllCaches() {
+ final CacheManager cacheMgr = cachePool.getCacheManager();
+ final String[] cacheNames = cacheMgr.getCacheNames();
+ Arrays.sort(cacheNames);
+ final Ehcache[] r = new Ehcache[cacheNames.length];
+ for (int i = 0; i < cacheNames.length; i++) {
+ r[i] = cacheMgr.getEhcache(cacheNames[i]);
+ }
+ return r;
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
new file mode 100644
index 0000000000..36de24324f
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/DefaultCommandModule.java
@@ -0,0 +1,54 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.CommandModule;
+import com.google.gerrit.sshd.CommandName;
+import com.google.gerrit.sshd.Commands;
+import com.google.gerrit.sshd.DispatchCommandProvider;
+
+
+/** Register the basic commands any Gerrit server should support. */
+public class DefaultCommandModule extends CommandModule {
+ @Override
+ protected void configure() {
+ final CommandName git = Commands.named("git");
+ final CommandName gerrit = Commands.named("gerrit");
+
+ // The following commands can be ran on a server in either Master or Slave
+ // mode. If a command should only be used on a server in one mode, but not
+ // both, it should be bound in both MasterCommandModule and
+ // SlaveCommandModule.
+
+ command(gerrit).toProvider(new DispatchCommandProvider(gerrit));
+ command(gerrit, "flush-caches").to(AdminFlushCaches.class);
+ command(gerrit, "ls-projects").to(ListProjects.class);
+ command(gerrit, "show-caches").to(AdminShowCaches.class);
+ command(gerrit, "show-connections").to(AdminShowConnections.class);
+ command(gerrit, "show-queue").to(AdminShowQueue.class);
+
+ command(git).toProvider(new DispatchCommandProvider(git));
+ command(git, "receive-pack").to(Commands.key(gerrit, "receive-pack"));
+ command(git, "upload-pack").to(Upload.class);
+
+ command("scp").to(ScpCommand.class);
+
+ // Honor the legacy hyphenated forms as aliases for the non-hyphenated forms
+ //
+ command("git-upload-pack").to(Commands.key(git, "upload-pack"));
+ command("git-receive-pack").to(Commands.key(git, "receive-pack"));
+ command("gerrit-receive-pack").to(Commands.key(git, "receive-pack"));
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ErrorSlaveMode.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ErrorSlaveMode.java
new file mode 100644
index 0000000000..a008b6b309
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ErrorSlaveMode.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.BaseCommand;
+
+import java.io.IOException;
+
+/**
+ * A command which just throws an error because it shouldn't be ran on this
+ * server. This is used when a user tries to run a command on a server in Slave
+ * Mode, but the command only applies to the Master server.
+ */
+final class ErrorSlaveMode extends BaseCommand {
+ @Override
+ public void start() {
+ String msg =
+ "error: That command is disabled on this server.\n\n"
+ + "Please use the master server URL.\n";
+ try {
+ err.write(msg.getBytes(ENC));
+ err.flush();
+ } catch (IOException e) {
+ // Ignore errors writing to the client
+ }
+ onExit(1);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
new file mode 100644
index 0000000000..b5d2b50b8f
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
@@ -0,0 +1,76 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.reviewdb.Project;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.WildProjectName;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.sshd.BaseCommand;
+import com.google.gwtorm.client.OrmException;
+import com.google.inject.Inject;
+
+import java.io.PrintWriter;
+
+final class ListProjects extends BaseCommand {
+ @Inject
+ private ReviewDb db;
+
+ @Inject
+ private IdentifiedUser currentUser;
+
+ @Inject
+ private ProjectCache projectCache;
+
+ @Inject
+ @WildProjectName
+ private Project.NameKey wildProject;
+
+ @Override
+ public void start() {
+ startThread(new CommandRunnable() {
+ @Override
+ public void run() throws Exception {
+ parseCommandLine();
+ ListProjects.this.display();
+ }
+ });
+ }
+
+ private void display() throws Failure {
+ final PrintWriter stdout = toPrintWriter(out);
+ try {
+ for (final Project p : db.projects().all()) {
+ if (p.getNameKey().equals(wildProject)) {
+ // This project "doesn't exist". At least not as a repository.
+ //
+ continue;
+ }
+
+ final ProjectState e = projectCache.get(p.getNameKey());
+ if (e != null && e.controlFor(currentUser).isVisible()) {
+ stdout.print(p.getName());
+ stdout.println();
+ }
+ }
+ } catch (OrmException e) {
+ throw new Failure(1, "fatal: database error", e);
+ } finally {
+ stdout.flush();
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java
new file mode 100644
index 0000000000..fd0ad50668
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/MasterCommandModule.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.CommandModule;
+import com.google.gerrit.sshd.CommandName;
+import com.google.gerrit.sshd.Commands;
+
+
+/** Register the commands a Gerrit server in master mode supports. */
+public class MasterCommandModule extends CommandModule {
+ @Override
+ protected void configure() {
+ final CommandName gerrit = Commands.named("gerrit");
+
+ command(gerrit, "approve").to(ApproveCommand.class);
+ command(gerrit, "create-project").to(AdminCreateProject.class);
+ command(gerrit, "receive-pack").to(Receive.class);
+ command(gerrit, "replicate").to(AdminReplicate.class);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
new file mode 100644
index 0000000000..614148a89c
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
@@ -0,0 +1,1440 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_UPDATE;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_ANNOTATED;
+import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_ANY;
+
+import com.google.gerrit.common.PageLinks;
+import com.google.gerrit.common.data.ApprovalType;
+import com.google.gerrit.common.data.ApprovalTypes;
+import com.google.gerrit.common.errors.NoSuchAccountException;
+import com.google.gerrit.reviewdb.AbstractAgreement;
+import com.google.gerrit.reviewdb.Account;
+import com.google.gerrit.reviewdb.AccountAgreement;
+import com.google.gerrit.reviewdb.AccountGroup;
+import com.google.gerrit.reviewdb.AccountGroupAgreement;
+import com.google.gerrit.reviewdb.ApprovalCategory;
+import com.google.gerrit.reviewdb.Branch;
+import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.reviewdb.ChangeMessage;
+import com.google.gerrit.reviewdb.ContributorAgreement;
+import com.google.gerrit.reviewdb.PatchSet;
+import com.google.gerrit.reviewdb.PatchSetApproval;
+import com.google.gerrit.reviewdb.PatchSetInfo;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.ChangeUtil;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.config.Nullable;
+import com.google.gerrit.server.git.PatchSetImporter;
+import com.google.gerrit.server.git.ReplicationQueue;
+import com.google.gerrit.server.mail.CreateChangeSender;
+import com.google.gerrit.server.mail.EmailException;
+import com.google.gerrit.server.mail.MergedSender;
+import com.google.gerrit.server.mail.ReplacePatchSetSender;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.OrmRunnable;
+import com.google.gwtorm.client.Transaction;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.revwalk.FooterKey;
+import org.eclipse.jgit.revwalk.FooterLine;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevFlag;
+import org.eclipse.jgit.revwalk.RevFlagSet;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.transport.PostReceiveHook;
+import org.eclipse.jgit.transport.PreReceiveHook;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
+import org.eclipse.jgit.transport.ReceiveCommand.Type;
+import org.kohsuke.args4j.Option;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+
+/** Receives change upload over SSH using the Git receive-pack protocol. */
+final class Receive extends AbstractGitCommand {
+ private static final Logger log = LoggerFactory.getLogger(Receive.class);
+
+ private static final String NEW_CHANGE = "refs/for/";
+ private static final Pattern NEW_PATCHSET =
+ Pattern.compile("^refs/changes/(?:[0-9][0-9]/)?([1-9][0-9]*)(?:/new)?$");
+
+ private static final FooterKey REVIEWED_BY = new FooterKey("Reviewed-by");
+ private static final FooterKey TESTED_BY = new FooterKey("Tested-by");
+ private static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
+
+ private final Set<Account.Id> reviewerId = new HashSet<Account.Id>();
+ private final Set<Account.Id> ccId = new HashSet<Account.Id>();
+
+ @Option(name = "--reviewer", aliases = {"--re"}, multiValued = true, metaVar = "EMAIL", usage = "request reviewer for change(s)")
+ void addReviewer(final Account.Id id) {
+ reviewerId.add(id);
+ }
+
+ @Option(name = "--cc", aliases = {}, multiValued = true, metaVar = "EMAIL", usage = "CC user on change(s)")
+ void addCC(final Account.Id id) {
+ ccId.add(id);
+ }
+
+ @Inject
+ private IdentifiedUser.GenericFactory identifiedUserFactory;
+
+ @Inject
+ private IdentifiedUser currentUser;
+
+ @Inject
+ private ReviewDb db;
+
+ @Inject
+ private ApprovalTypes approvalTypes;
+
+ @Inject
+ private AccountResolver accountResolver;
+
+ @Inject
+ private CreateChangeSender.Factory createChangeSenderFactory;
+
+ @Inject
+ private MergedSender.Factory mergedSenderFactory;
+
+ @Inject
+ private ReplacePatchSetSender.Factory replacePatchSetFactory;
+
+ @Inject
+ private ReplicationQueue replication;
+
+ @Inject
+ private PatchSetImporter.Factory importFactory;
+
+ @Inject
+ private PatchSetInfoFactory patchSetInfoFactory;
+
+ @Inject
+ @CanonicalWebUrl
+ @Nullable
+ private String canonicalWebUrl;
+
+ @Inject
+ @GerritPersonIdent
+ private PersonIdent gerritIdent;
+
+ private ReceivePack rp;
+ private PersonIdent refLogIdent;
+ private ReceiveCommand newChange;
+ private Branch.NameKey destBranch;
+
+ private final List<Change.Id> allNewChanges = new ArrayList<Change.Id>();
+ private final Map<Change.Id, ReplaceRequest> replaceByChange =
+ new HashMap<Change.Id, ReplaceRequest>();
+ private final Map<RevCommit, ReplaceRequest> replaceByCommit =
+ new HashMap<RevCommit, ReplaceRequest>();
+
+ private Map<ObjectId, Ref> refsById;
+
+ protected boolean canUpload() {
+ return canPerform(ApprovalCategory.READ, (short) 2);
+ }
+
+ @Override
+ protected void runImpl() throws IOException, Failure {
+ if (!canUpload()) {
+ final String reqName = project.getName();
+ throw new Failure(1, "fatal: Upload denied for project '" + reqName + "'",
+ new SecurityException("Account lacks Upload permission"));
+ }
+
+ if (project.isUseContributorAgreements()) {
+ verifyActiveContributorAgreement();
+ }
+ refLogIdent = currentUser.newPersonIdent();
+
+ verifyProjectVisible("reviewer", reviewerId);
+ verifyProjectVisible("CC", ccId);
+
+ rp = new ReceivePack(repo);
+ rp.setAllowCreates(true);
+ rp.setAllowDeletes(true);
+ rp.setAllowNonFastForwards(true);
+ rp.setCheckReceivedObjects(true);
+ rp.setRefLogIdent(refLogIdent);
+ rp.setPreReceiveHook(new PreReceiveHook() {
+ public void onPreReceive(final ReceivePack arg0,
+ final Collection<ReceiveCommand> commands) {
+ parseCommands(commands);
+ if (newChange != null
+ && newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ createNewChanges();
+ }
+ doReplaces();
+ }
+ });
+ rp.setPostReceiveHook(new PostReceiveHook() {
+ public void onPostReceive(final ReceivePack arg0,
+ final Collection<ReceiveCommand> commands) {
+ for (final ReceiveCommand c : commands) {
+ if (c.getResult() == Result.OK) {
+ if (isHead(c)) {
+ switch (c.getType()) {
+ case CREATE:
+ autoCloseChanges(c);
+ break;
+ case DELETE:
+ break;
+ case UPDATE:
+ case UPDATE_NONFASTFORWARD:
+ autoCloseChanges(c);
+ break;
+ }
+ }
+
+ if (isHead(c) || isTag(c)) {
+ // We only schedule heads and tags for replication.
+ // Change refs are scheduled when they are created.
+ //
+ replication.scheduleUpdate(project.getNameKey(), c.getRefName());
+ }
+ }
+ }
+ }
+ });
+ rp.receive(in, out, err);
+
+ if (!allNewChanges.isEmpty() && canonicalWebUrl != null) {
+ // Make sure there isn't anything buffered; we want to give the
+ // push client a chance to display its status report before we
+ // show our own messages on standard error.
+ //
+ out.flush();
+
+ final String url = canonicalWebUrl;
+ final PrintWriter msg = toPrintWriter(err);
+ msg.write("\nNew Changes:\n");
+ for (final Change.Id c : allNewChanges) {
+ msg.write(" " + url + c.get() + "\n");
+ }
+ msg.write('\n');
+ msg.flush();
+ }
+ }
+
+ private void verifyProjectVisible(final String type, final Set<Account.Id> who)
+ throws UnloggedFailure {
+ for (final Account.Id id : who) {
+ final IdentifiedUser user = identifiedUserFactory.create(id);
+ if (!projectControl.forUser(user).isVisible()) {
+ throw new UnloggedFailure(1, type + " "
+ + user.getAccount().getFullName() + " cannot access the project");
+ }
+ }
+ }
+
+ private void verifyActiveContributorAgreement() throws Failure {
+ AbstractAgreement bestAgreement = null;
+ ContributorAgreement bestCla = null;
+ try {
+ OUTER: for (AccountGroup.Id groupId : currentUser.getEffectiveGroups()) {
+ for (final AccountGroupAgreement a : db.accountGroupAgreements()
+ .byGroup(groupId)) {
+ final ContributorAgreement cla =
+ db.contributorAgreements().get(a.getAgreementId());
+ if (cla == null) {
+ continue;
+ }
+
+ bestAgreement = a;
+ bestCla = cla;
+ break OUTER;
+ }
+ }
+
+ if (bestAgreement == null) {
+ for (final AccountAgreement a : db.accountAgreements().byAccount(
+ currentUser.getAccountId()).toList()) {
+ final ContributorAgreement cla =
+ db.contributorAgreements().get(a.getAgreementId());
+ if (cla == null) {
+ continue;
+ }
+
+ bestAgreement = a;
+ bestCla = cla;
+ break;
+ }
+ }
+ } catch (OrmException e) {
+ throw new Failure(1, "fatal: database error", e);
+ }
+
+ if (bestCla != null && !bestCla.isActive()) {
+ final StringBuilder msg = new StringBuilder();
+ msg.append("\nfatal: ");
+ msg.append(bestCla.getShortName());
+ msg.append(" contributor agreement is expired.\n");
+ if (canonicalWebUrl != null) {
+ msg.append("\nPlease complete a new agreement");
+ msg.append(":\n\n ");
+ msg.append(canonicalWebUrl);
+ msg.append("#");
+ msg.append(PageLinks.SETTINGS_AGREEMENTS);
+ msg.append("\n");
+ }
+ msg.append("\n");
+ throw new UnloggedFailure(1, msg.toString());
+ }
+
+ if (bestCla != null && bestCla.isRequireContactInformation()) {
+ boolean fail = false;
+ fail |= missing(currentUser.getAccount().getFullName());
+ fail |= missing(currentUser.getAccount().getPreferredEmail());
+ fail |= !currentUser.getAccount().isContactFiled();
+
+ if (fail) {
+ final StringBuilder msg = new StringBuilder();
+ msg.append("\nfatal: ");
+ msg.append(bestCla.getShortName());
+ msg.append(" contributor agreement requires");
+ msg.append(" current contact information.\n");
+ if (canonicalWebUrl != null) {
+ msg.append("\nPlease review your contact information");
+ msg.append(":\n\n ");
+ msg.append(canonicalWebUrl);
+ msg.append("#");
+ msg.append(PageLinks.SETTINGS_CONTACT);
+ msg.append("\n");
+ }
+ msg.append("\n");
+ throw new UnloggedFailure(1, msg.toString());
+ }
+ }
+
+ if (bestAgreement != null) {
+ switch (bestAgreement.getStatus()) {
+ case VERIFIED:
+ return;
+ case REJECTED:
+ throw new UnloggedFailure(1, "\nfatal: " + bestCla.getShortName()
+ + " contributor agreement was rejected."
+ + "\n (rejected on " + bestAgreement.getReviewedOn()
+ + ")\n");
+ case NEW:
+ throw new UnloggedFailure(1, "\nfatal: " + bestCla.getShortName()
+ + " contributor agreement is still pending review.\n");
+ }
+ }
+
+ final StringBuilder msg = new StringBuilder();
+ msg.append("\nfatal: A Contributor Agreement"
+ + " must be completed before uploading");
+ if (canonicalWebUrl != null) {
+ msg.append(":\n\n ");
+ msg.append(canonicalWebUrl);
+ msg.append("#");
+ msg.append(PageLinks.SETTINGS_AGREEMENTS);
+ msg.append("\n");
+ } else {
+ msg.append(".");
+ }
+ msg.append("\n");
+ throw new UnloggedFailure(1, msg.toString());
+ }
+
+ private static boolean missing(final String value) {
+ return value == null || value.trim().equals("");
+ }
+
+ private Account.Id toAccountId(final String nameOrEmail) throws OrmException,
+ NoSuchAccountException {
+ final Account a = accountResolver.find(nameOrEmail);
+ if (a == null) {
+ throw new NoSuchAccountException("\"" + nameOrEmail
+ + "\" is not registered");
+ }
+ return a.getId();
+ }
+
+ private void parseCommands(final Collection<ReceiveCommand> commands) {
+ for (final ReceiveCommand cmd : commands) {
+ if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
+ // Already rejected by the core receive process.
+ //
+ continue;
+ }
+
+ if (cmd.getRefName().startsWith(NEW_CHANGE)) {
+ parseNewChangeCommand(cmd);
+ continue;
+ }
+
+ final Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
+ if (m.matches()) {
+ // The referenced change must exist and must still be open.
+ //
+ final Change.Id changeId = Change.Id.parse(m.group(1));
+ parseReplaceCommand(cmd, changeId);
+ continue;
+ }
+
+ switch (cmd.getType()) {
+ case CREATE:
+ parseCreate(cmd);
+ continue;
+
+ case UPDATE:
+ parseUpdate(cmd);
+ continue;
+
+ case DELETE:
+ case UPDATE_NONFASTFORWARD:
+ parseRewindOrDelete(cmd);
+ continue;
+ }
+
+ // Everything else is bogus as far as we are concerned.
+ //
+ reject(cmd);
+ }
+ }
+
+ private void parseCreate(final ReceiveCommand cmd) {
+ if (projectControl.canCreateRef(cmd.getRefName())) {
+ if (isTag(cmd)) {
+ parseCreateTag(cmd);
+ }
+
+ } else {
+ reject(cmd);
+ }
+ }
+
+ private void parseCreateTag(final ReceiveCommand cmd) {
+ try {
+ final RevObject obj = rp.getRevWalk().parseAny(cmd.getNewId());
+ if (!(obj instanceof RevTag)) {
+ reject(cmd, "not annotated tag");
+ return;
+ }
+
+ if (canPerform(PUSH_TAG, PUSH_TAG_ANY)) {
+ // If we can push any tag, validation is sufficient at this point.
+ //
+ return;
+ }
+
+ final RevTag tag = (RevTag) obj;
+ final PersonIdent tagger = tag.getTaggerIdent();
+ if (tagger == null) {
+ reject(cmd, "no tagger");
+ return;
+ }
+
+ final String email = tagger.getEmailAddress();
+ if (!currentUser.getEmailAddresses().contains(email)) {
+ reject(cmd, "invalid tagger " + email);
+ return;
+ }
+
+ if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
+ // Signed tags are currently assumed valid, as we don't have a GnuPG
+ // key ring to validate them against, and we might be missing the
+ // necessary (but currently optional) BouncyCastle Crypto libraries.
+ //
+ } else if (canPerform(PUSH_TAG, PUSH_TAG_ANNOTATED)) {
+ // User is permitted to push an unsigned annotated tag.
+ //
+ } else {
+ reject(cmd, "must be signed");
+ return;
+ }
+
+ // Let the core receive process handle it
+ //
+ } catch (IOException e) {
+ log.error("Bad tag " + cmd.getRefName() + " " + cmd.getNewId().name(), e);
+ reject(cmd, "invalid object");
+ }
+ }
+
+ private void parseUpdate(final ReceiveCommand cmd) {
+ if (isHead(cmd) && canPerform(PUSH_HEAD, PUSH_HEAD_UPDATE)) {
+ // Let the core receive process handle it
+ } else {
+ reject(cmd);
+ }
+ }
+
+ private void parseRewindOrDelete(final ReceiveCommand cmd) {
+ if (isHead(cmd) && cmd.getType() == Type.DELETE
+ && projectControl.canDeleteRef(cmd.getRefName())) {
+ // Let the core receive process handle it
+
+ } else if (isHead(cmd) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
+ // Let the core receive process handle it
+
+ } else if (isHead(cmd) && cmd.getType() == Type.UPDATE_NONFASTFORWARD) {
+ cmd.setResult(ReceiveCommand.Result.REJECTED_NONFASTFORWARD);
+
+ } else {
+ reject(cmd);
+ }
+ }
+
+ private void parseNewChangeCommand(final ReceiveCommand cmd) {
+ // Permit exactly one new change request per push.
+ //
+ if (newChange != null) {
+ reject(cmd, "duplicate request");
+ return;
+ }
+
+ newChange = cmd;
+ String destBranchName = cmd.getRefName().substring(NEW_CHANGE.length());
+ if (!destBranchName.startsWith(Constants.R_REFS)) {
+ destBranchName = Constants.R_HEADS + destBranchName;
+ }
+
+ if (rp.getAdvertisedRefs().containsKey(destBranchName)) {
+ // We advertised the branch to the client so we know
+ // the branch exists. Target this branch for the upload.
+ //
+ destBranch = new Branch.NameKey(project.getNameKey(), destBranchName);
+
+ } else {
+ // We didn't advertise the branch, because it doesn't exist yet.
+ // Allow it anyway if HEAD is a symbolic reference to the name.
+ //
+ final String head;
+ try {
+ head = repo.getFullBranch();
+ } catch (IOException e) {
+ log.error("Cannot read HEAD symref", e);
+ reject(cmd, "internal error");
+ return;
+ }
+
+ if (head.equals(destBranchName)) {
+ destBranch = new Branch.NameKey(project.getNameKey(), destBranchName);
+ }
+ }
+
+ if (destBranch == null) {
+ String n = destBranchName;
+ if (n.startsWith(Constants.R_HEADS))
+ n = n.substring(Constants.R_HEADS.length());
+ reject(cmd, "branch " + n + " not found");
+ return;
+ }
+
+ // Validate that the new commits are connected with the existing heads
+ // or tags of this repository. If they aren't, we want to abort. We do
+ // this check by coloring the tip CONNECTED and letting a RevWalk push
+ // that color through the graph until it reaches at least one of our
+ // already existing heads or tags. We then test to see if that color
+ // made it back onto that set.
+ //
+ try {
+ final RevWalk walk = rp.getRevWalk();
+
+ final RevFlag SIDE_NEW = walk.newFlag("NEW");
+ final RevFlag SIDE_HAVE = walk.newFlag("HAVE");
+ final RevFlagSet COMMON = new RevFlagSet();
+ COMMON.add(SIDE_NEW);
+ COMMON.add(SIDE_HAVE);
+ walk.carry(COMMON);
+
+ walk.reset();
+ walk.sort(RevSort.TOPO);
+ walk.sort(RevSort.REVERSE, true);
+
+ final RevCommit tip = walk.parseCommit(newChange.getNewId());
+ tip.add(SIDE_NEW);
+ walk.markStart(tip);
+
+ boolean haveHeads = false;
+ for (final Ref r : rp.getAdvertisedRefs().values()) {
+ if (isHead(r) || isTag(r)) {
+ try {
+ final RevCommit h = walk.parseCommit(r.getObjectId());
+ h.add(SIDE_HAVE);
+ walk.markStart(h);
+ haveHeads = true;
+ } catch (IOException e) {
+ continue;
+ }
+ }
+ }
+
+ if (haveHeads) {
+ boolean isConnected = false;
+ RevCommit c;
+ while ((c = walk.next()) != null) {
+ if (c.hasAll(COMMON)) {
+ isConnected = true;
+ break;
+ }
+ }
+ if (!isConnected) {
+ reject(newChange, "no common ancestry");
+ return;
+ }
+ }
+ } catch (IOException e) {
+ newChange.setResult(Result.REJECTED_MISSING_OBJECT);
+ log.error("Invalid pack upload; one or more objects weren't sent", e);
+ return;
+ }
+ }
+
+ private void parseReplaceCommand(final ReceiveCommand cmd,
+ final Change.Id changeId) {
+ if (cmd.getType() != ReceiveCommand.Type.CREATE) {
+ reject(cmd, "invalid usage");
+ return;
+ }
+
+ final RevCommit newCommit;
+ try {
+ newCommit = rp.getRevWalk().parseCommit(cmd.getNewId());
+ } catch (IOException e) {
+ log.error("Cannot parse " + cmd.getNewId().name() + " as commit", e);
+ reject(cmd, "invalid commit");
+ return;
+ }
+
+ final Change changeEnt;
+ try {
+ changeEnt = db.changes().get(changeId);
+ } catch (OrmException e) {
+ log.error("Cannot lookup existing change " + changeId, e);
+ reject(cmd, "database error");
+ return;
+ }
+ if (changeEnt == null) {
+ reject(cmd, "change " + changeId + " not found");
+ return;
+ }
+ if (!project.getNameKey().equals(changeEnt.getProject())) {
+ reject(cmd, "change " + changeId + " not found");
+ return;
+ }
+
+ requestReplace(cmd, changeEnt, newCommit);
+ }
+
+ private void requestReplace(final ReceiveCommand cmd, final Change change,
+ final RevCommit newCommit) {
+ if (change.getStatus().isClosed()) {
+ reject(cmd, "change " + change.getId() + " closed");
+ return;
+ }
+
+ final ReplaceRequest req =
+ new ReplaceRequest(change.getId(), newCommit, cmd);
+ if (replaceByChange.containsKey(req.ontoChange)) {
+ reject(cmd, "duplicate request");
+ return;
+ }
+ if (replaceByCommit.containsKey(req.newCommit)) {
+ reject(cmd, "duplicate request");
+ return;
+ }
+ replaceByChange.put(req.ontoChange, req);
+ replaceByCommit.put(req.newCommit, req);
+ }
+
+ private void createNewChanges() {
+ final List<RevCommit> toCreate = new ArrayList<RevCommit>();
+ final RevWalk walk = rp.getRevWalk();
+ walk.reset();
+ walk.sort(RevSort.TOPO);
+ walk.sort(RevSort.REVERSE, true);
+ try {
+ walk.markStart(walk.parseCommit(newChange.getNewId()));
+ for (final Ref r : rp.getAdvertisedRefs().values()) {
+ try {
+ walk.markUninteresting(walk.parseCommit(r.getObjectId()));
+ } catch (IOException e) {
+ continue;
+ }
+ }
+
+ for (;;) {
+ final RevCommit c = walk.next();
+ if (c == null) {
+ break;
+ }
+ if (replaceByCommit.containsKey(c)) {
+ // This commit was already scheduled to replace an existing PatchSet.
+ //
+ continue;
+ }
+ if (!validCommitter(newChange, c)) {
+ // Not a change the user can propose? Abort as early as possible.
+ //
+ return;
+ }
+
+ final List<String> idList = c.getFooterLines(CHANGE_ID);
+ if (!idList.isEmpty()) {
+ final Change.Key key = new Change.Key(idList.get(idList.size() - 1));
+ final List<Change> changes =
+ db.changes().byProjectKey(project.getNameKey(), key).toList();
+ if (changes.size() > 1) {
+ // WTF, multiple changes in this project have the same key?
+ // Since the commit is new, the user should recreate it with
+ // a different Change-Id. In practice, we should never see
+ // this error message as Change-Id should be unique.
+ //
+ reject(newChange, key.get() + " has duplicates");
+ return;
+
+ }
+
+ if (changes.size() == 1) {
+ // Schedule as a replacement to this one matching change.
+ //
+ requestReplace(newChange, changes.get(0), c);
+ continue;
+ }
+ }
+
+ toCreate.add(c);
+ }
+ } catch (IOException e) {
+ // Should never happen, the core receive process would have
+ // identified the missing object earlier before we got control.
+ //
+ newChange.setResult(Result.REJECTED_MISSING_OBJECT);
+ log.error("Invalid pack upload; one or more objects weren't sent", e);
+ return;
+ } catch (OrmException e) {
+ log.error("Cannot query database to locate prior changes", e);
+ reject(newChange, "database error");
+ return;
+ }
+
+ if (toCreate.isEmpty() && replaceByChange.isEmpty()) {
+ reject(newChange, "no new changes");
+ return;
+ }
+
+ for (final RevCommit c : toCreate) {
+ try {
+ createChange(walk, c);
+ } catch (IOException e) {
+ log.error("Error computing patch of commit " + c.name(), e);
+ reject(newChange, "diff error");
+ return;
+ } catch (OrmException e) {
+ log.error("Error creating change for commit " + c.name(), e);
+ reject(newChange, "database error");
+ return;
+ }
+ }
+ newChange.setResult(ReceiveCommand.Result.OK);
+ }
+
+ private void createChange(final RevWalk walk, final RevCommit c)
+ throws OrmException, IOException {
+ walk.parseBody(c);
+
+ final Account.Id me = currentUser.getAccountId();
+ Change.Key changeKey = new Change.Key("I" + c.name());
+ final Set<Account.Id> reviewers = new HashSet<Account.Id>(reviewerId);
+ final Set<Account.Id> cc = new HashSet<Account.Id>(ccId);
+ for (final FooterLine footerLine : c.getFooterLines()) {
+ try {
+ if (footerLine.matches(CHANGE_ID)) {
+ final String v = footerLine.getValue().trim();
+ if (v.matches("^I[0-9a-f]{8,}.*$")) {
+ changeKey = new Change.Key(v);
+ }
+ } else if (isReviewer(footerLine)) {
+ reviewers.add(toAccountId(footerLine.getValue().trim()));
+ } else if (footerLine.matches(FooterKey.CC)) {
+ cc.add(toAccountId(footerLine.getValue().trim()));
+ }
+ } catch (NoSuchAccountException e) {
+ continue;
+ }
+ }
+ reviewers.remove(me);
+ cc.remove(me);
+ cc.removeAll(reviewers);
+
+ final Transaction txn = db.beginTransaction();
+ final Change change =
+ new Change(changeKey, new Change.Id(db.nextChangeId()), me, destBranch);
+ final PatchSet ps = new PatchSet(change.newPatchSetId());
+ ps.setCreatedOn(change.getCreatedOn());
+ ps.setUploader(me);
+
+ final PatchSetImporter imp = importFactory.create(db, c, ps, true);
+ imp.setTransaction(txn);
+ imp.run();
+
+ change.setCurrentPatchSet(imp.getPatchSetInfo());
+ ChangeUtil.updated(change);
+ db.changes().insert(Collections.singleton(change), txn);
+
+ final Set<Account.Id> haveApprovals = new HashSet<Account.Id>();
+ final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
+ haveApprovals.add(me);
+
+ if (allTypes.size() > 0) {
+ final Account.Id authorId =
+ imp.getPatchSetInfo().getAuthor() != null ? imp.getPatchSetInfo()
+ .getAuthor().getAccount() : null;
+ final Account.Id committerId =
+ imp.getPatchSetInfo().getCommitter() != null ? imp.getPatchSetInfo()
+ .getCommitter().getAccount() : null;
+ final ApprovalCategory.Id catId =
+ allTypes.get(allTypes.size() - 1).getCategory().getId();
+ if (authorId != null && haveApprovals.add(authorId)) {
+ insertDummyApproval(change, ps.getId(), authorId, catId, db, txn);
+ }
+ if (committerId != null && haveApprovals.add(committerId)) {
+ insertDummyApproval(change, ps.getId(), committerId, catId, db, txn);
+ }
+ for (final Account.Id reviewer : reviewers) {
+ if (haveApprovals.add(reviewer)) {
+ insertDummyApproval(change, ps.getId(), reviewer, catId, db, txn);
+ }
+ }
+ }
+
+ txn.commit();
+
+ final RefUpdate ru = repo.updateRef(ps.getRefName());
+ ru.setNewObjectId(c);
+ ru.disableRefLog();
+ if (ru.update(walk) != RefUpdate.Result.NEW) {
+ throw new IOException("Failed to create ref " + ps.getRefName() + " in "
+ + repo.getDirectory() + ": " + ru.getResult());
+ }
+ replication.scheduleUpdate(project.getNameKey(), ru.getName());
+
+ allNewChanges.add(change.getId());
+
+ try {
+ final CreateChangeSender cm;
+ cm = createChangeSenderFactory.create(change);
+ cm.setFrom(me);
+ cm.setPatchSet(ps, imp.getPatchSetInfo());
+ cm.setReviewDb(db);
+ cm.addReviewers(reviewers);
+ cm.addExtraCC(cc);
+ cm.send();
+ } catch (EmailException e) {
+ log.error("Cannot send email for new change " + change.getId(), e);
+ }
+ }
+
+ private static boolean isReviewer(final FooterLine candidateFooterLine) {
+ return candidateFooterLine.matches(FooterKey.SIGNED_OFF_BY)
+ || candidateFooterLine.matches(FooterKey.ACKED_BY)
+ || candidateFooterLine.matches(REVIEWED_BY)
+ || candidateFooterLine.matches(TESTED_BY);
+ }
+
+ private void doReplaces() {
+ for (final ReplaceRequest request : replaceByChange.values()) {
+ try {
+ doReplace(request);
+ } catch (IOException err) {
+ log.error("Error computing replacement patch for change "
+ + request.ontoChange + ", commit " + request.newCommit.name(), err);
+ reject(request.cmd, "diff error");
+ } catch (OrmException err) {
+ log.error("Error storing replacement patch for change "
+ + request.ontoChange + ", commit " + request.newCommit.name(), err);
+ reject(request.cmd, "database error");
+ }
+ if (request.cmd.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
+ log.error("Replacement patch for change " + request.ontoChange
+ + ", commit " + request.newCommit.name() + " wasn't attempted."
+ + " This is a bug in the receive process implementation.");
+ reject(request.cmd, "internal error");
+ }
+ }
+ }
+
+ private PatchSet.Id doReplace(final ReplaceRequest request)
+ throws IOException, OrmException {
+ final RevCommit c = request.newCommit;
+ rp.getRevWalk().parseBody(c);
+ if (!validCommitter(request.cmd, c)) {
+ return null;
+ }
+
+ final Account.Id me = currentUser.getAccountId();
+ final Set<Account.Id> reviewers = new HashSet<Account.Id>(reviewerId);
+ final Set<Account.Id> cc = new HashSet<Account.Id>(ccId);
+ for (final FooterLine footerLine : c.getFooterLines()) {
+ try {
+ if (isReviewer(footerLine)) {
+ reviewers.add(toAccountId(footerLine.getValue().trim()));
+ } else if (footerLine.matches(FooterKey.CC)) {
+ cc.add(toAccountId(footerLine.getValue().trim()));
+ }
+ } catch (NoSuchAccountException e) {
+ continue;
+ }
+ }
+ reviewers.remove(me);
+ cc.remove(me);
+ cc.removeAll(reviewers);
+
+ final ReplaceResult result;
+
+ final Set<Account.Id> oldReviewers = new HashSet<Account.Id>();
+ final Set<Account.Id> oldCC = new HashSet<Account.Id>();
+
+ result = db.run(new OrmRunnable<ReplaceResult, ReviewDb>() {
+ public ReplaceResult run(final ReviewDb db, final Transaction txn,
+ final boolean isRetry) throws OrmException {
+ final Change change = db.changes().get(request.ontoChange);
+ if (change == null) {
+ reject(request.cmd, "change " + request.ontoChange + " not found");
+ return null;
+ }
+ if (change.getStatus().isClosed()) {
+ reject(request.cmd, "change " + request.ontoChange + " closed");
+ return null;
+ }
+
+ final PatchSet.Id priorPatchSet = change.currentPatchSetId();
+ for (final PatchSet ps : db.patchSets().byChange(request.ontoChange)) {
+ if (ps.getRevision() == null) {
+ reject(request.cmd, "change state corrupt");
+ return null;
+ }
+
+ final String revIdStr = ps.getRevision().get();
+ final ObjectId commitId;
+ try {
+ commitId = ObjectId.fromString(revIdStr);
+ } catch (IllegalArgumentException e) {
+ log.warn("Invalid revision in " + ps.getId() + ": " + revIdStr);
+ reject(request.cmd, "change state corrupt");
+ return null;
+ }
+
+ try {
+ final RevCommit prior = rp.getRevWalk().parseCommit(commitId);
+
+ // Don't allow a change to directly depend upon itself. This is a
+ // very common error due to users making a new commit rather than
+ // amending when trying to address review comments.
+ //
+ if (rp.getRevWalk().isMergedInto(prior, c)) {
+ reject(request.cmd, "squash commits first");
+ return null;
+ }
+
+ // Don't allow the same commit to appear twice on the same change
+ //
+ if (c == prior) {
+ reject(request.cmd, "commit already exists");
+ return null;
+ }
+
+ // Don't allow the same tree if the commit message is unmodified,
+ // else warn that only the message changed.
+ //
+ if (priorPatchSet.equals(ps.getId())
+ && c.getTree() == prior.getTree()) {
+ rp.getRevWalk().parseBody(prior);
+ if (c.getFullMessage().equals(prior.getFullMessage())) {
+ reject(request.cmd, "no changes made");
+ return null;
+ } else {
+ err.write(Constants
+ .encode("warning: Only commit message changed in "
+ + change.getKey().abbreviate() + "\n"));
+ }
+ }
+ } catch (IOException e) {
+ log.error("Change " + change.getId() + " missing " + revIdStr, e);
+ reject(request.cmd, "change state corrupt");
+ return null;
+ }
+ }
+
+ final PatchSet ps = new PatchSet(change.newPatchSetId());
+ ps.setCreatedOn(new Timestamp(System.currentTimeMillis()));
+ ps.setUploader(currentUser.getAccountId());
+
+ final PatchSetImporter imp = importFactory.create(db, c, ps, true);
+ imp.setTransaction(txn);
+ imp.run();
+
+ final Ref mergedInto = findMergedInto(change.getDest().get(), c);
+ final ReplaceResult result = new ReplaceResult();
+ result.mergedIntoRef = mergedInto != null ? mergedInto.getName() : null;
+ result.change = change;
+ result.patchSet = ps;
+ result.info = imp.getPatchSetInfo();
+
+ final Account.Id authorId =
+ imp.getPatchSetInfo().getAuthor() != null ? imp.getPatchSetInfo()
+ .getAuthor().getAccount() : null;
+ final Account.Id committerId =
+ imp.getPatchSetInfo().getCommitter() != null ? imp
+ .getPatchSetInfo().getCommitter().getAccount() : null;
+
+ boolean haveAuthor = false;
+ boolean haveCommitter = false;
+ final Set<Account.Id> haveApprovals = new HashSet<Account.Id>();
+
+ oldReviewers.clear();
+ oldCC.clear();
+
+ for (PatchSetApproval a : db.patchSetApprovals().byChange(
+ change.getId())) {
+ haveApprovals.add(a.getAccountId());
+
+ if (a.getValue() != 0) {
+ oldReviewers.add(a.getAccountId());
+ } else {
+ oldCC.add(a.getAccountId());
+ }
+
+ final ApprovalType type =
+ approvalTypes.getApprovalType(a.getCategoryId());
+ if (a.getPatchSetId().equals(priorPatchSet)
+ && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
+ // If there was a negative vote on the prior patch set, carry it
+ // into this patch set.
+ //
+ db.patchSetApprovals()
+ .insert(
+ Collections.singleton(new PatchSetApproval(ps.getId(), a)),
+ txn);
+ }
+
+ if (!haveAuthor && authorId != null
+ && a.getAccountId().equals(authorId)) {
+ haveAuthor = true;
+ }
+ if (!haveCommitter && committerId != null
+ && a.getAccountId().equals(committerId)) {
+ haveCommitter = true;
+ }
+ }
+
+ final ChangeMessage msg =
+ new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
+ .messageUUID(db)), me, ps.getCreatedOn());
+ msg.setMessage("Uploaded patch set " + ps.getPatchSetId() + ".");
+ db.changeMessages().insert(Collections.singleton(msg), txn);
+ result.msg = msg;
+
+ if (result.mergedIntoRef != null) {
+ // Change was already submitted to a branch, close it.
+ //
+ markChangeMergedByPush(db, txn, result);
+ } else {
+ // Change should be new, so it can go through review again.
+ //
+ change.setStatus(Change.Status.NEW);
+ change.setCurrentPatchSet(imp.getPatchSetInfo());
+ ChangeUtil.updated(change);
+ db.changes().update(Collections.singleton(change), txn);
+ }
+
+ final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
+ if (allTypes.size() > 0) {
+ final ApprovalCategory.Id catId =
+ allTypes.get(allTypes.size() - 1).getCategory().getId();
+ if (authorId != null && haveApprovals.add(authorId)) {
+ insertDummyApproval(result, authorId, catId, db, txn);
+ }
+ if (committerId != null && haveApprovals.add(committerId)) {
+ insertDummyApproval(result, committerId, catId, db, txn);
+ }
+ for (final Account.Id reviewer : reviewers) {
+ if (haveApprovals.add(reviewer)) {
+ insertDummyApproval(result, reviewer, catId, db, txn);
+ }
+ }
+ }
+ return result;
+ }
+ });
+ if (result != null) {
+ final PatchSet ps = result.patchSet;
+ final RefUpdate ru = repo.updateRef(ps.getRefName());
+ ru.setNewObjectId(c);
+ ru.disableRefLog();
+ if (ru.update(rp.getRevWalk()) != RefUpdate.Result.NEW) {
+ throw new IOException("Failed to create ref " + ps.getRefName()
+ + " in " + repo.getDirectory() + ": " + ru.getResult());
+ }
+ replication.scheduleUpdate(project.getNameKey(), ru.getName());
+ request.cmd.setResult(ReceiveCommand.Result.OK);
+
+ try {
+ final ReplacePatchSetSender cm;
+ cm = replacePatchSetFactory.create(result.change);
+ cm.setFrom(me);
+ cm.setPatchSet(ps, result.info);
+ cm.setChangeMessage(result.msg);
+ cm.setReviewDb(db);
+ cm.addReviewers(reviewers);
+ cm.addExtraCC(cc);
+ cm.addReviewers(oldReviewers);
+ cm.addExtraCC(oldCC);
+ cm.send();
+ } catch (EmailException e) {
+ log.error("Cannot send email for new patch set " + ps.getId(), e);
+ }
+ }
+ sendMergedEmail(result);
+ return result != null ? result.info.getKey() : null;
+ }
+
+ private void insertDummyApproval(final ReplaceResult result,
+ final Account.Id forAccount, final ApprovalCategory.Id catId,
+ final ReviewDb db, final Transaction txn) throws OrmException {
+ insertDummyApproval(result.change, result.patchSet.getId(), forAccount,
+ catId, db, txn);
+ }
+
+ private void insertDummyApproval(final Change change, final PatchSet.Id psId,
+ final Account.Id forAccount, final ApprovalCategory.Id catId,
+ final ReviewDb db, final Transaction txn) throws OrmException {
+ final PatchSetApproval ca =
+ new PatchSetApproval(new PatchSetApproval.Key(psId, forAccount, catId),
+ (short) 0);
+ ca.cache(change);
+ db.patchSetApprovals().insert(Collections.singleton(ca), txn);
+ }
+
+ private Ref findMergedInto(final String first, final RevCommit commit) {
+ try {
+ final Map<String, Ref> all = repo.getAllRefs();
+ Ref firstRef = all.get(first);
+ if (firstRef != null && isMergedInto(commit, firstRef)) {
+ return firstRef;
+ }
+ for (Ref ref : all.values()) {
+ if (isHead(ref)) {
+ if (isMergedInto(commit, ref)) {
+ return ref;
+ }
+ }
+ }
+ return null;
+ } catch (IOException e) {
+ log.warn("Can't check for already submitted change", e);
+ return null;
+ }
+ }
+
+ private boolean isMergedInto(final RevCommit commit, final Ref ref)
+ throws IOException {
+ final RevWalk rw = rp.getRevWalk();
+ return rw.isMergedInto(commit, rw.parseCommit(ref.getObjectId()));
+ }
+
+ private static class ReplaceRequest {
+ final Change.Id ontoChange;
+ final RevCommit newCommit;
+ final ReceiveCommand cmd;
+
+ ReplaceRequest(final Change.Id toChange, final RevCommit newCommit,
+ final ReceiveCommand cmd) {
+ this.ontoChange = toChange;
+ this.newCommit = newCommit;
+ this.cmd = cmd;
+ }
+ }
+
+ private static class ReplaceResult {
+ Change change;
+ PatchSet patchSet;
+ PatchSetInfo info;
+ ChangeMessage msg;
+ String mergedIntoRef;
+ }
+
+ private boolean validCommitter(final ReceiveCommand cmd, final RevCommit c)
+ throws MissingObjectException, IOException {
+ rp.getRevWalk().parseBody(c);
+ final PersonIdent committer = c.getCommitterIdent();
+ final PersonIdent author = c.getAuthorIdent();
+
+ // Don't allow the user to amend a merge created by Gerrit Code Review.
+ // This seems to happen all too often, due to users not paying any
+ // attention to what they are doing.
+ //
+ if (c.getParentCount() > 1
+ && author.getName().equals(gerritIdent.getName())
+ && author.getEmailAddress().equals(gerritIdent.getEmailAddress())) {
+ reject(cmd, "do not amend merges not made by you");
+ return false;
+ }
+
+ // Require that committer matches the uploader.
+ //
+ if (!currentUser.getEmailAddresses().contains(committer.getEmailAddress())) {
+ reject(cmd, "you are not committer " + committer.getEmailAddress());
+ return false;
+ }
+
+ if (project.isUseSignedOffBy()) {
+ // If the project wants Signed-off-by / Acked-by lines, verify we
+ // have them for the blamable parties involved on this change.
+ //
+ boolean sboAuthor = false, sboCommitter = false, sboMe = false;
+ for (final FooterLine footer : c.getFooterLines()) {
+ if (footer.matches(FooterKey.SIGNED_OFF_BY)) {
+ final String e = footer.getEmailAddress();
+ if (e != null) {
+ sboAuthor |= author.getEmailAddress().equals(e);
+ sboCommitter |= committer.getEmailAddress().equals(e);
+ sboMe |= currentUser.getEmailAddresses().contains(e);
+ }
+ }
+ }
+ if (!sboAuthor && !sboCommitter && !sboMe) {
+ reject(cmd, "not Signed-off-by author/committer/uploader");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void autoCloseChanges(final ReceiveCommand cmd) {
+ final RevWalk rw = rp.getRevWalk();
+ try {
+ rw.reset();
+ rw.markStart(rw.parseCommit(cmd.getNewId()));
+ if (!ObjectId.zeroId().equals(cmd.getOldId())) {
+ rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
+ }
+
+ final Map<ObjectId, Ref> byCommit = changeRefsById();
+ final Map<Change.Key, Change.Id> byKey = openChangesByKey();
+ final List<ReplaceRequest> toClose = new ArrayList<ReplaceRequest>();
+ RevCommit c;
+ while ((c = rw.next()) != null) {
+ final Ref ref = byCommit.get(c.copy());
+ if (ref != null) {
+ closeChange(cmd, PatchSet.Id.fromRef(ref.getName()), c);
+ continue;
+ }
+
+ rw.parseBody(c);
+ for (final String changeId : c.getFooterLines(CHANGE_ID)) {
+ final Change.Id onto = byKey.get(new Change.Key(changeId));
+ if (onto != null) {
+ toClose.add(new ReplaceRequest(onto, c, cmd));
+ break;
+ }
+ }
+ }
+
+ for (final ReplaceRequest req : toClose) {
+ final PatchSet.Id psi = doReplace(req);
+ if (psi != null) {
+ closeChange(req.cmd, psi, req.newCommit);
+ } else {
+ log.warn("Replacement of Change-Id " + req.ontoChange
+ + " with commit " + req.newCommit.name()
+ + " did not import the new patch set.");
+ }
+ }
+ } catch (IOException e) {
+ log.error("Can't scan for changes to close", e);
+ } catch (OrmException e) {
+ log.error("Can't scan for changes to close", e);
+ }
+ }
+
+ private void closeChange(final ReceiveCommand cmd, final PatchSet.Id psi,
+ final RevCommit commit) throws OrmException {
+ final String refName = cmd.getRefName();
+ final Change.Id cid = psi.getParentKey();
+ final ReplaceResult result =
+ db.run(new OrmRunnable<ReplaceResult, ReviewDb>() {
+ @Override
+ public ReplaceResult run(ReviewDb db, Transaction txn, boolean retry)
+ throws OrmException {
+ final Change change = db.changes().get(cid);
+ final PatchSet ps = db.patchSets().get(psi);
+ if (change == null || ps == null) {
+ log.warn(project.getName() + " " + psi + " is missing");
+ return null;
+ }
+
+ if (change.getStatus() == Change.Status.MERGED) {
+ // If its already merged, don't make further updates, it
+ // might just be moving from an experimental branch into
+ // a more stable branch.
+ //
+ return null;
+ }
+
+ final ReplaceResult result = new ReplaceResult();
+ result.change = change;
+ result.patchSet = ps;
+ result.info = patchSetInfoFactory.get(commit, psi);
+ result.mergedIntoRef = refName;
+ markChangeMergedByPush(db, txn, result);
+ return result;
+ }
+ });
+ sendMergedEmail(result);
+ }
+
+ private Map<ObjectId, Ref> changeRefsById() {
+ if (refsById == null) {
+ refsById = new HashMap<ObjectId, Ref>();
+ for (final Ref r : repo.getAllRefs().values()) {
+ if (PatchSet.isRef(r.getName())) {
+ refsById.put(r.getObjectId(), r);
+ }
+ }
+ }
+ return refsById;
+ }
+
+ private Map<Change.Key, Change.Id> openChangesByKey() throws OrmException {
+ final Map<Change.Key, Change.Id> r = new HashMap<Change.Key, Change.Id>();
+ for (Change c : db.changes().byProjectOpenAll(project.getNameKey())) {
+ r.put(c.getKey(), c.getId());
+ }
+ return r;
+ }
+
+ private void markChangeMergedByPush(final ReviewDb db, final Transaction txn,
+ final ReplaceResult result) throws OrmException {
+ final Change change = result.change;
+ final String mergedIntoRef = result.mergedIntoRef;
+
+ change.setCurrentPatchSet(result.info);
+ change.setStatus(Change.Status.MERGED);
+ ChangeUtil.updated(change);
+
+ final List<PatchSetApproval> approvals =
+ db.patchSetApprovals().byChange(change.getId()).toList();
+ for (PatchSetApproval a : approvals) {
+ a.cache(change);
+ }
+
+ final StringBuilder msgBuf = new StringBuilder();
+ msgBuf.append("Change has been successfully pushed");
+ if (!mergedIntoRef.equals(change.getDest().get())) {
+ msgBuf.append(" into ");
+ if (mergedIntoRef.startsWith(Constants.R_HEADS)) {
+ msgBuf.append("branch ");
+ msgBuf.append(repo.shortenRefName(mergedIntoRef));
+ } else {
+ msgBuf.append(mergedIntoRef);
+ }
+ }
+ msgBuf.append(".");
+ final ChangeMessage msg =
+ new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
+ .messageUUID(db)), currentUser.getAccountId());
+ msg.setMessage(msgBuf.toString());
+
+ db.patchSetApprovals().update(approvals, txn);
+ db.changeMessages().insert(Collections.singleton(msg), txn);
+ db.changes().update(Collections.singleton(change), txn);
+ }
+
+ private void sendMergedEmail(final ReplaceResult result) {
+ if (result != null && result.mergedIntoRef != null) {
+ try {
+ final MergedSender cm = mergedSenderFactory.create(result.change);
+ cm.setFrom(currentUser.getAccountId());
+ cm.setReviewDb(db);
+ cm.setPatchSet(result.patchSet, result.info);
+ cm.setDest(new Branch.NameKey(project.getNameKey(),
+ result.mergedIntoRef));
+ cm.send();
+ } catch (EmailException e) {
+ final PatchSet.Id psi = result.patchSet.getId();
+ log.error("Cannot send email for submitted patch set " + psi, e);
+ }
+ }
+ }
+
+ private boolean canPerform(final ApprovalCategory.Id actionId, final short val) {
+ return projectControl.canPerform(actionId, val);
+ }
+
+ private static void reject(final ReceiveCommand cmd) {
+ reject(cmd, "prohibited by Gerrit");
+ }
+
+ private static void reject(final ReceiveCommand cmd, final String why) {
+ cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
+ }
+
+ private static boolean isTag(final Ref ref) {
+ return ref.getName().startsWith(Constants.R_TAGS);
+ }
+
+ private static boolean isTag(final ReceiveCommand cmd) {
+ return cmd.getRefName().startsWith(Constants.R_TAGS);
+ }
+
+ private static boolean isHead(final Ref ref) {
+ return ref.getName().startsWith(Constants.R_HEADS);
+ }
+
+ private static boolean isHead(final ReceiveCommand cmd) {
+ return cmd.getRefName().startsWith(Constants.R_HEADS);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java
new file mode 100644
index 0000000000..2fa177f226
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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.
+ */
+
+/*
+ * NB: This code was primarly ripped out of MINA SSHD.
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+package com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.common.Version;
+import com.google.gerrit.sshd.BaseCommand;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.eclipse.jgit.util.RawParseUtils;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+final class ScpCommand extends BaseCommand {
+ private static final String TYPE_DIR = "D";
+ private static final String TYPE_FILE = "C";
+ private static final Logger log = LoggerFactory.getLogger(ScpCommand.class);
+
+ private boolean opt_r;
+ private boolean opt_t;
+ private boolean opt_f;
+ private boolean opt_v;
+ private boolean opt_p;
+ private String root;
+
+ private TreeMap<String, Entry> toc;
+ private IOException error;
+
+ @Override
+ public void setCommandLine(final String line) {
+ super.setCommandLine(line);
+
+ final String[] args = line.split(" ");
+ root = "";
+ for (int i = 0; i < args.length; i++) {
+ if (args[i].charAt(0) == '-') {
+ for (int j = 1; j < args[i].length(); j++) {
+ switch (args[i].charAt(j)) {
+ case 'f':
+ opt_f = true;
+ break;
+ case 'p':
+ opt_p = true;
+ break;
+ case 'r':
+ opt_r = true;
+ break;
+ case 't':
+ opt_t = true;
+ break;
+ case 'v':
+ opt_v = true;
+ break;
+ }
+ }
+ } else if (i == args.length - 1) {
+ root = args[args.length - 1];
+ }
+ }
+ if (!opt_f && !opt_t) {
+ error = new IOException("Either -f or -t option should be set");
+ }
+ }
+
+ @Override
+ public void start() {
+ startThread(new Runnable() {
+ public void run() {
+ runImp();
+ }
+ });
+ }
+
+ private void runImp() {
+ try {
+ if (error != null) {
+ throw error;
+ }
+
+ readToc();
+ if (opt_f) {
+ if (root.startsWith("/")) {
+ root = root.substring(1);
+ }
+ if (root.endsWith("/")) {
+ root = root.substring(0, root.length() - 1);
+ }
+ if (root.equals(".")) {
+ root = "";
+ }
+
+ final Entry ent = toc.get(root);
+ if (ent == null) {
+ throw new IOException(root + " not found");
+
+ } else if (TYPE_FILE.equals(ent.type)) {
+ readFile(ent);
+
+ } else if (TYPE_DIR.equals(ent.type)) {
+ if (!opt_r) {
+ throw new IOException(root + " not a regular file");
+ }
+ readDir(ent);
+ } else {
+ throw new IOException(root + " not supported");
+ }
+ } else {
+ throw new IOException("Unsupported mode");
+ }
+ } catch (IOException e) {
+ if (e.getClass() == IOException.class
+ && "Pipe closed".equals(e.getMessage())) {
+ // Ignore a pipe closed error, its the user disconnecting from us
+ // while we are waiting for them to stalk.
+ //
+ return;
+ }
+
+ try {
+ out.write(2);
+ out.write(e.getMessage().getBytes());
+ out.write('\n');
+ out.flush();
+ } catch (IOException e2) {
+ // Ignore
+ }
+ log.debug("Error in scp command", e);
+ }
+ }
+
+ private void readToc() throws IOException {
+ toc = new TreeMap<String, Entry>();
+ final BufferedReader br =
+ new BufferedReader(new InputStreamReader(new ByteArrayInputStream(
+ read("TOC")), "UTF-8"));
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (line.length() > 0 && !line.startsWith("#")) {
+ final Entry e = new Entry(TYPE_FILE, line);
+ toc.put(e.path, e);
+ }
+ }
+
+ final List<Entry> all = new ArrayList<Entry>(toc.values());
+ for (Entry e : all) {
+ String path = dirOf(e.path);
+ while (path != null) {
+ Entry d = toc.get(path);
+ if (d == null) {
+ d = new Entry(TYPE_DIR, 0755, path);
+ toc.put(d.path, d);
+ }
+ d.children.add(e);
+ path = dirOf(path);
+ e = d;
+ }
+ }
+
+ final Entry top = new Entry(TYPE_DIR, 0755, "");
+ for (Entry e : toc.values()) {
+ if (dirOf(e.path) == null) {
+ top.children.add(e);
+ }
+ }
+ toc.put(top.path, top);
+ }
+
+ private String readLine() throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (;;) {
+ int c = in.read();
+ if (c == '\n') {
+ return baos.toString();
+ } else if (c == -1) {
+ throw new IOException("End of stream");
+ } else {
+ baos.write(c);
+ }
+ }
+ }
+
+ private static String nameOf(String path) {
+ final int s = path.lastIndexOf('/');
+ return s < 0 ? path : path.substring(s + 1);
+ }
+
+ private static String dirOf(String path) {
+ final int s = path.lastIndexOf('/');
+ return s < 0 ? null : path.substring(0, s);
+ }
+
+ private static byte[] read(String path) {
+ final InputStream in =
+ ScpCommand.class.getClassLoader().getResourceAsStream(
+ "com/google/gerrit/sshd/scproot/" + path);
+ if (in == null) {
+ return null;
+ }
+ try {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ final byte[] buf = new byte[8192];
+ int n;
+ while ((n = in.read(buf, 0, buf.length)) > 0) {
+ out.write(buf, 0, n);
+ }
+ } finally {
+ in.close();
+ }
+ return out.toByteArray();
+ } catch (Exception e) {
+ log.debug("Cannot read " + path, e);
+ return null;
+ }
+ }
+
+ private void readFile(final Entry ent) throws IOException {
+ byte[] data = read(ent.path);
+ if (data == null) {
+ throw new FileNotFoundException(ent.path);
+ }
+
+ if (data.length > 3 && data[0] == '#' && data[1] == '!' && data[2] == '/') {
+ // Embed Gerrit's version number into the top of the script.
+ //
+ final String version = Version.getVersion();
+ final int lf = RawParseUtils.nextLF(data, 0);
+ if (version != null && lf < data.length) {
+ final byte[] versionHeader =
+ ("# From Gerrit Code Review " + version + "\n").getBytes("UTF-8");
+ final ByteArrayOutputStream buf;
+ buf = new ByteArrayOutputStream(data.length + versionHeader.length);
+ buf.write(data, 0, lf);
+ buf.write(versionHeader);
+ buf.write(data, lf, data.length - lf);
+ data = buf.toByteArray();
+ }
+ }
+
+ header(ent, data.length);
+ readAck();
+
+ out.write(data);
+ ack();
+ readAck();
+ }
+
+ private void readDir(final Entry dir) throws IOException {
+ header(dir, 0);
+ readAck();
+
+ for (Entry e : dir.children) {
+ if (TYPE_DIR.equals(e.type)) {
+ readDir(e);
+ } else {
+ readFile(e);
+ }
+ }
+
+ out.write("E\n".getBytes("UTF-8"));
+ out.flush();
+ readAck();
+ }
+
+ private void header(final Entry dir, final int len) throws IOException,
+ UnsupportedEncodingException {
+ final StringBuilder buf = new StringBuilder();
+ buf.append(dir.type);
+ buf.append(dir.mode); // perms
+ buf.append(" ");
+ buf.append(len); // length
+ buf.append(" ");
+ buf.append(nameOf(dir.path));
+ buf.append("\n");
+ out.write(buf.toString().getBytes("UTF-8"));
+ out.flush();
+ }
+
+ private void ack() throws IOException {
+ out.write(0);
+ out.flush();
+ }
+
+ private void readAck() throws IOException {
+ switch (in.read()) {
+ case 0:
+ break;
+ case 1:
+ log.debug("Received warning: " + readLine());
+ break;
+ case 2:
+ throw new IOException("Received nack: " + readLine());
+ }
+ }
+
+ private static class Entry {
+ String type;
+ String mode;
+ String path;
+ List<Entry> children;
+
+ Entry(String type, String line) {
+ this.type = type;
+ int s = line.indexOf(' ');
+ mode = line.substring(0, s);
+ path = line.substring(s + 1);
+
+ if (!mode.startsWith("0")) {
+ mode = "0" + mode;
+ }
+ }
+
+ Entry(String type, int mode, String path) {
+ this.type = type;
+ this.mode = "0" + Integer.toOctalString(mode);
+ this.path = path;
+ this.children = new ArrayList<Entry>();
+ }
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SlaveCommandModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SlaveCommandModule.java
new file mode 100644
index 0000000000..c0074a99cb
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SlaveCommandModule.java
@@ -0,0 +1,33 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import com.google.gerrit.sshd.CommandModule;
+import com.google.gerrit.sshd.CommandName;
+import com.google.gerrit.sshd.Commands;
+
+
+/** Register the commands a Gerrit server in slave mode supports. */
+public class SlaveCommandModule extends CommandModule {
+ @Override
+ protected void configure() {
+ final CommandName gerrit = Commands.named("gerrit");
+
+ command(gerrit, "approve").to(ErrorSlaveMode.class);
+ command(gerrit, "create-project").to(ErrorSlaveMode.class);
+ command(gerrit, "receive-pack").to(ErrorSlaveMode.class);
+ command(gerrit, "replicate").to(ErrorSlaveMode.class);
+ }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
new file mode 100644
index 0000000000..389b838e02
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.commands;
+
+import org.eclipse.jgit.transport.UploadPack;
+
+import java.io.IOException;
+
+/** Publishes Git repositories over SSH using the Git upload-pack protocol. */
+final class Upload extends AbstractGitCommand {
+ @Override
+ protected void runImpl() throws IOException {
+ final UploadPack up = new UploadPack(repo);
+ up.upload(in, out, err);
+ }
+}
diff --git a/src/main/java/com/google/gerrit/server/ssh/scproot/TOC b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/TOC
index f6d71f78b8..f6d71f78b8 100644
--- a/src/main/java/com/google/gerrit/server/ssh/scproot/TOC
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/TOC
diff --git a/src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/bin/gerrit-cherry-pick
index 007faa4056..007faa4056 100644
--- a/src/main/java/com/google/gerrit/server/ssh/scproot/bin/gerrit-cherry-pick
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/bin/gerrit-cherry-pick
diff --git a/src/main/java/com/google/gerrit/server/ssh/scproot/hooks/commit-msg b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/hooks/commit-msg
index e74537dec1..e74537dec1 100644
--- a/src/main/java/com/google/gerrit/server/ssh/scproot/hooks/commit-msg
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/scproot/hooks/commit-msg
diff --git a/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/CommitMsgHookTest.java b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/CommitMsgHookTest.java
new file mode 100644
index 0000000000..1ed79b92e7
--- /dev/null
+++ b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/CommitMsgHookTest.java
@@ -0,0 +1,367 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.sshd.scproot.hooks;
+
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.Commit;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectWriter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.RefUpdate;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class CommitMsgHookTest extends HookTestCase {
+ private final String SOB1 = "Signed-off-by: J Author <ja@example.com>\n";
+ private final String SOB2 = "Signed-off-by: J Committer <jc@example.com>\n";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final Date when = author.getWhen();
+ final TimeZone tz = author.getTimeZone();
+
+ author = new PersonIdent("J. Author", "ja@example.com");
+ author = new PersonIdent(author, when, tz);
+
+ committer = new PersonIdent("J. Committer", "jc@example.com");
+ committer = new PersonIdent(committer, when, tz);
+ }
+
+ public void testEmptyMessages() throws Exception {
+ // Empty input must yield empty output so commit will abort.
+ // Note we must consider different commit templates formats.
+ //
+ hookDoesNotModify("");
+ hookDoesNotModify(" ");
+ hookDoesNotModify("\n");
+ hookDoesNotModify("\n\n");
+ hookDoesNotModify(" \n ");
+
+ hookDoesNotModify("#");
+ hookDoesNotModify("#\n");
+ hookDoesNotModify("# on branch master\n# Untracked files:\n");
+ hookDoesNotModify("\n# on branch master\n# Untracked files:\n");
+ hookDoesNotModify("\n\n# on branch master\n# Untracked files:\n");
+
+ hookDoesNotModify("\n# on branch master\ndiff --git a/src b/src\n"
+ + "new file mode 100644\nindex 0000000..c78b7f0\n");
+ }
+
+ public void testChangeIdAlreadySet() throws Exception {
+ // If a Change-Id is already present in the footer, the hook must
+ // not modify the message but instead must leave the identity alone.
+ //
+ hookDoesNotModify("a\n" + //
+ "\n" + //
+ "Change-Id: Iaeac9b4149291060228ef0154db2985a31111335\n");
+ hookDoesNotModify("fix: this thing\n" + //
+ "\n" + //
+ "Change-Id: I388bdaf52ed05b55e62a22d0a20d2c1ae0d33e7e\n");
+ hookDoesNotModify("fix-a-widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: Id3bc5359d768a6400450283e12bdfb6cd135ea4b\n");
+ hookDoesNotModify("FIX: this thing\n" + //
+ "\n" + //
+ "Change-Id: I1b55098b5a2cce0b3f3da783dda50d5f79f873fa\n");
+ hookDoesNotModify("Fix-A-Widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: I4f4e2e1e8568ddc1509baecb8c1270a1fb4b6da7\n");
+ }
+
+ public void testTimeAltersId() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
+ call("a\n"));
+
+ tick();
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I3251906b99dda598a58a6346d8126237ee1ea800\n",//
+ call("a\n"));
+
+ tick();
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I69adf9208d828f41a3d7e41afbca63aff37c0c5c\n",//
+ call("a\n"));
+ }
+
+ public void testFirstParentAltersId() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
+ call("a\n"));
+
+ setHEAD();
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I51e86482bde7f92028541aaf724d3a3f996e7ea2\n",//
+ call("a\n"));
+ }
+
+ public void testDirCacheAltersId() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
+ call("a\n"));
+
+ final DirCacheBuilder builder = DirCache.lock(repository).builder();
+ builder.add(file("A"));
+ assertTrue(builder.commit());
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: If56597ea9759f23b070677ea6f064c60c38da631\n",//
+ call("a\n"));
+ }
+
+ public void testSingleLineMessages() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
+ call("a\n"));
+
+ assertEquals("fix: this thing\n" + //
+ "\n" + //
+ "Change-Id: I0f13d0e6c739ca3ae399a05a93792e80feb97f37\n",//
+ call("fix: this thing\n"));
+ assertEquals("fix-a-widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: I1a1a0c751e4273d532e4046a501a612b9b8a775e\n",//
+ call("fix-a-widget: this thing\n"));
+
+ assertEquals("FIX: this thing\n" + //
+ "\n" + //
+ "Change-Id: If816d944c57d3893b60cf10c65931fead1290d97\n",//
+ call("FIX: this thing\n"));
+ assertEquals("Fix-A-Widget: this thing\n" + //
+ "\n" + //
+ "Change-Id: I3e18d00cbda2ba1f73aeb63ed8c7d57d7fd16c76\n",//
+ call("Fix-A-Widget: this thing\n"));
+ }
+
+ public void testMultiLineMessagesWithoutFooter() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "b\n" + //
+ "\n" + //
+ "Change-Id: Id0b4f42d3d6fc1569595c9b97cb665e738486f5d\n",//
+ call("a\n" + "\n" + "b\n"));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "b\nc\nd\ne\n" + //
+ "\n" + //
+ "Change-Id: I7d237b20058a0f46cc3f5fabc4a0476877289d75\n",//
+ call("a\n" + "\n" + "b\nc\nd\ne\n"));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "b\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n",//
+ call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n"));
+ }
+
+ public void testSingleLineMessagesWithSignedOffBy() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
+ SOB1,//
+ call("a\n" + "\n" + SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
+ SOB1 + //
+ SOB2,//
+ call("a\n" + "\n" + SOB1 + SOB2));
+ }
+
+ public void testMultiLineMessagesWithSignedOffBy() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "b\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
+ SOB1,//
+ call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n" + "\n" + SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "b\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
+ SOB1 + //
+ SOB2,//
+ call("a\n" + //
+ "\n" + //
+ "b\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ SOB1 + //
+ SOB2));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "b: not a footer\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ "Change-Id: I8869aabd44b3017cd55d2d7e0d546a03e3931ee2\n" + //
+ SOB1 + //
+ SOB2,//
+ call("a\n" + //
+ "\n" + //
+ "b: not a footer\nc\nd\ne\n" + //
+ "\n" + //
+ "f\ng\nh\n" + //
+ "\n" + //
+ SOB1 + //
+ SOB2));
+ }
+
+ public void testNoteInMiddle() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "NOTE: This\n" + //
+ "does not fix it.\n" + //
+ "\n" + //
+ "Change-Id: I988a127969a6ee5e58db546aab74fc46e66847f8\n", //
+ call("a\n" + //
+ "\n" + //
+ "NOTE: This\n" + //
+ "does not fix it.\n"));
+ }
+
+ public void testKernelStyleFooter() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I1bd787f9e7590a2ac82b02c404c955ffb21877c4\n" + //
+ SOB1 + //
+ "[ja: Fixed\n" + //
+ " the indentation]\n" + //
+ SOB2, //
+ call("a\n" + //
+ "\n" + //
+ SOB1 + //
+ "[ja: Fixed\n" + //
+ " the indentation]\n" + //
+ SOB2));
+ }
+
+ public void testChangeIdAfterBugOrIssue() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Bug: 42\n" + //
+ "Change-Id: I8c0321227c4324e670b9ae8cf40eccc87af21b1b\n" + //
+ SOB1,//
+ call("a\n" + //
+ "\n" + //
+ "Bug: 42\n" + //
+ SOB1));
+
+ assertEquals("a\n" + //
+ "\n" + //
+ "Issue: 42\n" + //
+ "Change-Id: Ie66e07d89ae5b114c0975b49cf326e90331dd822\n" + //
+ SOB1,//
+ call("a\n" + //
+ "\n" + //
+ "Issue: 42\n" + //
+ SOB1));
+ }
+
+ public void testCommitDashV() throws Exception {
+ assertEquals("a\n" + //
+ "\n" + //
+ "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
+ SOB1 + //
+ SOB2, //
+ call("a\n" + //
+ "\n" + //
+ SOB1 + //
+ SOB2 + //
+ "\n" + //
+ "# on branch master\n" + //
+ "diff --git a/src b/src\n" + //
+ "new file mode 100644\n" + //
+ "index 0000000..c78b7f0\n"));
+ }
+
+ private void hookDoesNotModify(final String in) throws Exception {
+ assertEquals(in, call(in));
+ }
+
+ private String call(final String body) throws Exception {
+ final File tmp = write(body);
+ try {
+ final File hook = getHook("commit-msg");
+ assertEquals(0, runHook(repository, hook, tmp.getAbsolutePath()));
+ return read(tmp);
+ } finally {
+ tmp.delete();
+ }
+ }
+
+ private DirCacheEntry file(final String name) throws IOException {
+ final DirCacheEntry e = new DirCacheEntry(name);
+ e.setFileMode(FileMode.REGULAR_FILE);
+ e.setObjectId(writer().writeBlob(Constants.encode(name)));
+ return e;
+ }
+
+ private void setHEAD() throws Exception {
+ final ObjectWriter ow = writer();
+ final Commit commit = new Commit(repository);
+ commit.setTreeId(DirCache.newInCore().writeTree(ow));
+ commit.setAuthor(author);
+ commit.setCommitter(committer);
+ commit.setMessage("test\n");
+ final ObjectId commitId = ow.writeCommit(commit);
+
+ final RefUpdate ref = repository.updateRef(Constants.HEAD);
+ ref.setNewObjectId(commitId);
+ switch (ref.forceUpdate()) {
+ case NEW:
+ case FAST_FORWARD:
+ case FORCED:
+ case NO_CHANGE:
+ break;
+ default:
+ fail(Constants.HEAD + " did not change: " + ref.getResult());
+ }
+ }
+
+ private ObjectWriter writer() {
+ return new ObjectWriter(repository);
+ }
+}
diff --git a/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/HookTestCase.java b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/HookTestCase.java
new file mode 100644
index 0000000000..5902642220
--- /dev/null
+++ b/gerrit-sshd/src/test/java/com/google/gerrit/sshd/scproot/hooks/HookTestCase.java
@@ -0,0 +1,102 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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.
+//
+// Portions related to finding the hook script to execute are:
+// Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or
+// without modification, are permitted provided that the following
+// conditions are met:
+//
+// - Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// - Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// - Neither the name of the Git Development Community nor the
+// names of its contributors may be used to endorse or promote
+// products derived from this software without specific prior
+// written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.gerrit.sshd.scproot.hooks;
+
+import com.google.gerrit.testutil.LocalDiskRepositoryTestCase;
+
+import org.eclipse.jgit.lib.Repository;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+public abstract class HookTestCase extends LocalDiskRepositoryTestCase {
+ protected Repository repository;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ repository = createWorkRepository();
+ }
+
+ protected File getHook(final String name) {
+ final String scproot = "com/google/gerrit/sshd/scproot";
+ final String path = scproot + "/hooks/" + name;
+ final URL url = cl().getResource(path);
+ if (url == null) {
+ fail("Cannot locate " + path + " in CLASSPATH");
+ }
+
+ File hook;
+ try {
+ hook = new File(url.toURI());
+ } catch (URISyntaxException e) {
+ hook = new File(url.getPath());
+ }
+ if (!hook.isFile()) {
+ fail("Cannot locate " + path + " in CLASSPATH");
+ }
+
+ // The hook was copied out of our source control system into the
+ // target area by Java tools. Its not executable in the source
+ // are, nor did the copying Java program make it executable in the
+ // destination area. So we must force it to be executable.
+ //
+ final long time = hook.lastModified();
+ hook.setExecutable(true);
+ hook.setLastModified(time);
+ return hook;
+ }
+
+ private ClassLoader cl() {
+ return HookTestCase.class.getClassLoader();
+ }
+}
diff --git a/src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java b/gerrit-sshd/src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java
index 91879bddac..91879bddac 100644
--- a/src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java
+++ b/gerrit-sshd/src/test/java/com/google/gerrit/testutil/LocalDiskRepositoryTestCase.java
diff --git a/src/test/java/com/google/gerrit/testutil/MockSystemReader.java b/gerrit-sshd/src/test/java/com/google/gerrit/testutil/MockSystemReader.java
index 48952a5d50..48952a5d50 100644
--- a/src/test/java/com/google/gerrit/testutil/MockSystemReader.java
+++ b/gerrit-sshd/src/test/java/com/google/gerrit/testutil/MockSystemReader.java
diff --git a/gerrit-util-cli/.gitignore b/gerrit-util-cli/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-util-cli/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs b/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-util-cli/.settings/org.eclipse.core.runtime.prefs b/gerrit-util-cli/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-util-cli/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-util-cli/.settings/org.eclipse.jdt.core.prefs b/gerrit-util-cli/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-util-cli/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-util-cli/.settings/org.eclipse.jdt.ui.prefs b/gerrit-util-cli/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-util-cli/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-util-cli/pom.xml b/gerrit-util-cli/pom.xml
new file mode 100644
index 0000000000..6c3f25cbe9
--- /dev/null
+++ b/gerrit-util-cli/pom.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-util-cli</artifactId>
+ <name>Gerrit Code Review - Utility - CLI</name>
+
+ <description>
+ Utilities to support command line parsing
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice-assistedinject</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
new file mode 100644
index 0000000000..f83d852c26
--- /dev/null
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ *
+ * (Taken from JGit org.eclipse.jgit.pgm.opt.CmdLineParser.)
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.gerrit.util.cli;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.assistedinject.Assisted;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.IllegalAnnotationError;
+import org.kohsuke.args4j.Option;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+
+/**
+ * Extended command line parser which handles --foo=value arguments.
+ * <p>
+ * The args4j package does not natively handle --foo=value and instead prefers
+ * to see --foo value on the command line. Many users are used to the GNU style
+ * --foo=value long option, so we convert from the GNU style format to the
+ * args4j style format prior to invoking args4j for parsing.
+ */
+public class CmdLineParser {
+ public interface Factory {
+ CmdLineParser create(Object bean);
+ }
+
+ private final Injector injector;
+ private final MyParser parser;
+
+ /**
+ * Creates a new command line owner that parses arguments/options and set them
+ * into the given object.
+ *
+ * @param bean instance of a class annotated by
+ * {@link org.kohsuke.args4j.Option} and
+ * {@link org.kohsuke.args4j.Argument}. this object will receive
+ * values.
+ *
+ * @throws IllegalAnnotationError if the option bean class is using args4j
+ * annotations incorrectly.
+ */
+ @Inject
+ public CmdLineParser(final Injector injector, @Assisted final Object bean)
+ throws IllegalAnnotationError {
+ this.injector = injector;
+ this.parser = new MyParser(bean);
+ }
+
+ public void addArgument(Setter<?> setter, Argument a) {
+ parser.addArgument(setter, a);
+ }
+
+ public void addOption(Setter<?> setter, Option o) {
+ parser.addOption(setter, o);
+ }
+
+ public void printSingleLineUsage(Writer w, ResourceBundle rb) {
+ parser.printSingleLineUsage(w, rb);
+ }
+
+ public void printUsage(Writer out, ResourceBundle rb) {
+ parser.printUsage(out, rb);
+ }
+
+ public void parseArgument(final String... args) throws CmdLineException {
+ final ArrayList<String> tmp = new ArrayList<String>(args.length);
+ for (int argi = 0; argi < args.length; argi++) {
+ final String str = args[argi];
+ if (str.equals("--")) {
+ while (argi < args.length)
+ tmp.add(args[argi++]);
+ break;
+ }
+
+ if (str.startsWith("--")) {
+ final int eq = str.indexOf('=');
+ if (eq > 0) {
+ tmp.add(str.substring(0, eq));
+ tmp.add(str.substring(eq + 1));
+ continue;
+ }
+ }
+
+ tmp.add(str);
+ }
+
+ parser.parseArgument(tmp.toArray(new String[tmp.size()]));
+ }
+
+ private class MyParser extends org.kohsuke.args4j.CmdLineParser {
+ MyParser(final Object bean) {
+ super(bean);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected OptionHandler createOptionHandler(final OptionDef option,
+ final Setter setter) {
+ if (isHandlerSpecified(option) || isEnum(setter) || isPrimitive(setter)) {
+ return super.createOptionHandler(option, setter);
+ }
+
+ final Key<OptionHandlerFactory<?>> key =
+ OptionHandlerUtil.keyFor(setter.getType());
+ Injector i = injector;
+ while (i != null) {
+ if (i.getBindings().containsKey(key)) {
+ return i.getInstance(key).create(this, option, setter);
+ }
+ i = i.getParent();
+ }
+
+ return super.createOptionHandler(option, setter);
+ }
+
+ private boolean isHandlerSpecified(final OptionDef option) {
+ return option.handler() != OptionHandler.class;
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean isEnum(final Setter setter) {
+ return Enum.class.isAssignableFrom(setter.getType());
+ }
+
+ @SuppressWarnings("unchecked")
+ private boolean isPrimitive(final Setter setter) {
+ return setter.getType().isPrimitive();
+ }
+ }
+}
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerFactory.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerFactory.java
new file mode 100644
index 0000000000..ee5b9b6cab
--- /dev/null
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerFactory.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.util.cli;
+
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Setter;
+
+/** Creates an args4j OptionHandler through a Guice Injector. */
+public interface OptionHandlerFactory<T> {
+ @SuppressWarnings("unchecked")
+ OptionHandler create(org.kohsuke.args4j.CmdLineParser cmdLineParser,
+ OptionDef optionDef, Setter setter);
+}
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerUtil.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerUtil.java
new file mode 100644
index 0000000000..ab6739709b
--- /dev/null
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlerUtil.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.util.cli;
+
+import com.google.inject.Key;
+import com.google.inject.TypeLiteral;
+import com.google.inject.internal.MoreTypes.ParameterizedTypeImpl;
+
+import java.lang.reflect.Type;
+
+/** Utilities to support creating OptionHandler instances. */
+public class OptionHandlerUtil {
+ /** Generate a key for an {@link OptionHandlerFactory} in Guice. */
+ @SuppressWarnings("unchecked")
+ public static <T> Key<OptionHandlerFactory<T>> keyFor(final Class<T> valueType) {
+ final Type factoryType =
+ new ParameterizedTypeImpl(null, OptionHandlerFactory.class, valueType);
+
+ return (Key<OptionHandlerFactory<T>>) Key.get(TypeLiteral.get(factoryType));
+ }
+
+ private OptionHandlerUtil() {
+ }
+}
diff --git a/gerrit-util-ssl/.gitignore b/gerrit-util-ssl/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-util-ssl/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs b/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-util-ssl/.settings/org.eclipse.core.runtime.prefs b/gerrit-util-ssl/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-util-ssl/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-util-ssl/.settings/org.eclipse.jdt.core.prefs b/gerrit-util-ssl/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-util-ssl/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-util-ssl/.settings/org.eclipse.jdt.ui.prefs b/gerrit-util-ssl/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-util-ssl/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-util-ssl/pom.xml b/gerrit-util-ssl/pom.xml
new file mode 100644
index 0000000000..90cbb07c61
--- /dev/null
+++ b/gerrit-util-ssl/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-util-ssl</artifactId>
+ <name>Gerrit Code Review - Utility - SSL</name>
+
+ <description>
+ Utilities to support SSL based protocols
+ </description>
+</project>
diff --git a/gerrit-util-ssl/src/main/java/com/google/gerrit/util/ssl/BlindSSLSocketFactory.java b/gerrit-util-ssl/src/main/java/com/google/gerrit/util/ssl/BlindSSLSocketFactory.java
new file mode 100644
index 0000000000..ed31379afb
--- /dev/null
+++ b/gerrit-util-ssl/src/main/java/com/google/gerrit/util/ssl/BlindSSLSocketFactory.java
@@ -0,0 +1,112 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.util.ssl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+/** SSL socket factory that ignores SSL certificate validation. */
+public class BlindSSLSocketFactory extends SSLSocketFactory {
+ private static final BlindSSLSocketFactory INSTANCE;
+
+ static {
+ final X509TrustManager dummyTrustManager = new X509TrustManager() {
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+
+ public void checkClientTrusted(X509Certificate[] chain, String authType) {
+ }
+
+ public void checkServerTrusted(X509Certificate[] chain, String authType) {
+ }
+ };
+
+ try {
+ final SSLContext context = SSLContext.getInstance("SSL");
+ final TrustManager[] trustManagers = {dummyTrustManager};
+ final SecureRandom rng = new SecureRandom();
+ context.init(null, trustManagers, rng);
+ INSTANCE = new BlindSSLSocketFactory(context.getSocketFactory());
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException("Cannot create BlindSslSocketFactory", e);
+ }
+ }
+
+ public static SocketFactory getDefault() {
+ return INSTANCE;
+ }
+
+ private final SSLSocketFactory sslFactory;
+
+ private BlindSSLSocketFactory(final SSLSocketFactory sslFactory) {
+ this.sslFactory = sslFactory;
+ }
+
+ @Override
+ public Socket createSocket(Socket s, String host, int port, boolean autoClose)
+ throws IOException {
+ return sslFactory.createSocket(s, host, port, autoClose);
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return sslFactory.getDefaultCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return sslFactory.getSupportedCipherSuites();
+ }
+
+ @Override
+ public Socket createSocket() throws IOException {
+ return sslFactory.createSocket();
+ }
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException,
+ UnknownHostException {
+ return sslFactory.createSocket(host, port);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress host, int port) throws IOException {
+ return sslFactory.createSocket(host, port);
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localHost,
+ int localPort) throws IOException, UnknownHostException {
+ return sslFactory.createSocket(host, port, localHost, localPort);
+ }
+
+ @Override
+ public Socket createSocket(InetAddress address, int port,
+ InetAddress localAddress, int localPort) throws IOException {
+ return sslFactory.createSocket(address, port, localAddress, localPort);
+ }
+}
diff --git a/gerrit-war/.gitignore b/gerrit-war/.gitignore
new file mode 100644
index 0000000000..903c6c80f5
--- /dev/null
+++ b/gerrit-war/.gitignore
@@ -0,0 +1,4 @@
+/target
+/.classpath
+/.project
+/.settings/org.maven.ide.eclipse.prefs
diff --git a/gerrit-war/.settings/org.eclipse.core.resources.prefs b/gerrit-war/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..82eb859e3b
--- /dev/null
+++ b/gerrit-war/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+encoding/<project>=UTF-8
diff --git a/gerrit-war/.settings/org.eclipse.core.runtime.prefs b/gerrit-war/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 0000000000..8667cfd4a3
--- /dev/null
+++ b/gerrit-war/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@
+#Tue Sep 02 16:59:24 PDT 2008
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/gerrit-war/.settings/org.eclipse.jdt.core.prefs b/gerrit-war/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..04afc7fac5
--- /dev/null
+++ b/gerrit-war/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,268 @@
+#Tue May 12 17:44:13 PDT 2009
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-war/.settings/org.eclipse.jdt.ui.prefs b/gerrit-war/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000000..d4218a5fc0
--- /dev/null
+++ b/gerrit-war/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@
+#Wed Jul 29 11:31:38 PDT 2009
+eclipse.preferences.version=1
+editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
+formatter_profile=_Google Format
+formatter_settings_version=11
+org.eclipse.jdt.ui.ignorelowercasenames=true
+org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax;
+org.eclipse.jdt.ui.ondemandthreshold=99
+org.eclipse.jdt.ui.staticondemandthreshold=99
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/>
+sp_cleanup.add_default_serial_version_id=true
+sp_cleanup.add_generated_serial_version_id=false
+sp_cleanup.add_missing_annotations=false
+sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
+sp_cleanup.add_missing_nls_tags=false
+sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_serial_version_id=false
+sp_cleanup.always_use_blocks=true
+sp_cleanup.always_use_parentheses_in_expressions=false
+sp_cleanup.always_use_this_for_non_static_field_access=false
+sp_cleanup.always_use_this_for_non_static_method_access=false
+sp_cleanup.convert_to_enhanced_for_loop=false
+sp_cleanup.correct_indentation=false
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
+sp_cleanup.make_local_variable_final=true
+sp_cleanup.make_parameters_final=true
+sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
+sp_cleanup.make_variable_declarations_final=false
+sp_cleanup.never_use_blocks=false
+sp_cleanup.never_use_parentheses_in_expressions=true
+sp_cleanup.on_save_use_additional_actions=true
+sp_cleanup.organize_imports=false
+sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
+sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
+sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
+sp_cleanup.remove_private_constructors=true
+sp_cleanup.remove_trailing_whitespaces=true
+sp_cleanup.remove_trailing_whitespaces_all=true
+sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
+sp_cleanup.remove_unnecessary_casts=false
+sp_cleanup.remove_unnecessary_nls_tags=false
+sp_cleanup.remove_unused_imports=false
+sp_cleanup.remove_unused_local_variables=false
+sp_cleanup.remove_unused_private_fields=true
+sp_cleanup.remove_unused_private_members=false
+sp_cleanup.remove_unused_private_methods=true
+sp_cleanup.remove_unused_private_types=true
+sp_cleanup.sort_members=false
+sp_cleanup.sort_members_all=false
+sp_cleanup.use_blocks=false
+sp_cleanup.use_blocks_only_for_return_and_throw=false
+sp_cleanup.use_parentheses_in_expressions=false
+sp_cleanup.use_this_for_non_static_field_access=false
+sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
+sp_cleanup.use_this_for_non_static_method_access=false
+sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml
new file mode 100644
index 0000000000..113214bf60
--- /dev/null
+++ b/gerrit-war/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2009 The Android Open Source Project
+
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <version>2.0.25-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gerrit-war</artifactId>
+ <name>Gerrit Code Review - WAR</name>
+ <packaging>war</packaging>
+
+ <description>
+ Gerrit packaged as a standard web application archive
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-gwtui</artifactId>
+ <type>war</type>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-main</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcprov-jdk15</artifactId>
+ <version>140</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcpg-jdk15</artifactId>
+ <version>140</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-sshd</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-httpd</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-pgm</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <warName>gerrit-${project.version}</warName>
+ <archiveClasses>true</archiveClasses>
+ <archive>
+ <addMavenDescriptor>false</addMavenDescriptor>
+ <manifestEntries>
+ <Main-Class>Main</Main-Class>
+ <Implementation-Title>Gerrit Code Review</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ </manifestEntries>
+ </archive>
+ <overlays>
+ <overlay>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-main</artifactId>
+ <type>jar</type>
+ <includes>
+ <include>Main.class</include>
+ <include>com/google/gerrit/main/*.class</include>
+ </includes>
+ </overlay>
+ </overlays>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>fix-output</id>
+ <phase>process-classes</phase>
+ <configuration>
+ <tasks>
+ <property name="dst" location="${project.build.directory}/${project.build.finalName}" />
+ <property name="app" location="${dst}/gerrit" />
+
+ <copy tofile="${dst}/LICENSES.txt"
+ file="${basedir}/../Documentation/licenses.txt"
+ overwrite="true" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
new file mode 100644
index 0000000000..aa4763d3da
--- /dev/null
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -0,0 +1,228 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// 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 com.google.gerrit.httpd;
+
+import static com.google.inject.Stage.PRODUCTION;
+
+import com.google.gerrit.server.cache.CachePool;
+import com.google.gerrit.server.config.CanonicalWebUrlModule;
+import com.google.gerrit.server.config.DatabaseModule;
+import com.google.gerrit.server.config.GerritGlobalModule;
+import com.google.gerrit.server.git.PushAllProjectsOp;
+import com.google.gerrit.server.git.ReloadSubmitQueueOp;
+import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.sshd.SshDaemon;
+import com.google.gerrit.sshd.SshModule;
+import com.google.gerrit.sshd.commands.MasterCommandModule;
+import com.google.inject.ConfigurationException;
+import com.google.inject.CreationException;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.Provider;
+import com.google.inject.ProvisionException;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.spi.Message;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.http.HttpServletRequest;
+import javax.sql.DataSource;
+
+/** Configures the web application environment for Gerrit Code Review. */
+public class WebAppInitializer extends GuiceServletContextListener {
+ private static final Logger log =
+ LoggerFactory.getLogger(WebAppInitializer.class);
+
+ private Injector dbInjector;
+ private Injector sysInjector;
+ private Injector webInjector;
+ private Injector sshInjector;
+
+ private synchronized void init() {
+ if (sysInjector == null) {
+ try {
+ dbInjector = Guice.createInjector(PRODUCTION, new DatabaseModule());
+ } catch (CreationException ce) {
+ final Message first = ce.getErrorMessages().iterator().next();
+ final StringBuilder buf = new StringBuilder();
+ buf.append(first.getMessage());
+ Throwable why = first.getCause();
+ while (why != null) {
+ buf.append("\n caused by ");
+ buf.append(why.toString());
+ why = why.getCause();
+ }
+ if (first.getCause() != null) {
+ buf.append("\n");
+ buf.append("\nResolve above errors before continuing.");
+ buf.append("\nComplete stack trace follows:");
+ }
+ log.error(buf.toString(), first.getCause());
+ throw new CreationException(Collections.singleton(first));
+ }
+
+ sysInjector =
+ GerritGlobalModule.createInjector(dbInjector,
+ new CanonicalWebUrlModule() {
+ @Override
+ protected Class<? extends Provider<String>> provider() {
+ return HttpCanonicalWebUrlProvider.class;
+ }
+ });
+ sshInjector = createSshInjector();
+ webInjector = createWebInjector();
+
+ // Push the Provider<HttpServletRequest> down into the canonical
+ // URL provider. Its optional for that provider, but since we can
+ // supply one we should do so, in case the administrator has not
+ // setup the canonical URL in the configuration file.
+ //
+ // Note we have to do this manually as Guice failed to do the
+ // injection here because the HTTP environment is not visible
+ // to the core server modules.
+ //
+ sysInjector.getInstance(HttpCanonicalWebUrlProvider.class)
+ .setHttpServletRequest(
+ webInjector.getProvider(HttpServletRequest.class));
+ }
+ }
+
+ private Injector createSshInjector() {
+ return sysInjector.createChildInjector(new SshModule(),
+ new MasterCommandModule());
+ }
+
+ private Injector createWebInjector() {
+ final List<Module> modules = new ArrayList<Module>();
+ modules.add(sshInjector.getInstance(WebModule.class));
+ return sysInjector.createChildInjector(modules);
+ }
+
+ @Override
+ protected Injector getInjector() {
+ init();
+ return webInjector;
+ }
+
+ @Override
+ public void contextInitialized(final ServletContextEvent event) {
+ super.contextInitialized(event);
+ init();
+
+ try {
+ sysInjector.getInstance(CachePool.class).start();
+ } catch (ConfigurationException e) {
+ log.error("Unable to start CachePool", e);
+ } catch (ProvisionException e) {
+ log.error("Unable to start CachePool", e);
+ }
+
+ try {
+ sysInjector.getInstance(PushAllProjectsOp.Factory.class).create(null)
+ .start(30, TimeUnit.SECONDS);
+ } catch (ConfigurationException e) {
+ log.error("Unable to restart replication queue", e);
+ } catch (ProvisionException e) {
+ log.error("Unable to restart replication queue", e);
+ }
+
+ try {
+ sysInjector.getInstance(ReloadSubmitQueueOp.Factory.class).create()
+ .start(15, TimeUnit.SECONDS);
+ } catch (ConfigurationException e) {
+ log.error("Unable to restart merge queue", e);
+ } catch (ProvisionException e) {
+ log.error("Unable to restart merge queue", e);
+ }
+
+ try {
+ sshInjector.getInstance(SshDaemon.class).start();
+ } catch (ConfigurationException e) {
+ log.error("Unable to start SSHD", e);
+ } catch (ProvisionException e) {
+ log.error("Unable to start SSHD", e);
+ } catch (IOException e) {
+ log.error("Unable to start SSHD", e);
+ }
+ }
+
+ @Override
+ public void contextDestroyed(final ServletContextEvent event) {
+ try {
+ if (sshInjector != null) {
+ sshInjector.getInstance(SshDaemon.class).stop();
+ }
+ } catch (ConfigurationException e) {
+ } catch (ProvisionException e) {
+ }
+
+ try {
+ if (sysInjector != null) {
+ sysInjector.getInstance(WorkQueue.class).shutdown();
+ }
+ } catch (ConfigurationException e) {
+ } catch (ProvisionException e) {
+ }
+
+ try {
+ if (sysInjector != null) {
+ sysInjector.getInstance(CachePool.class).stop();
+ }
+ } catch (ConfigurationException e) {
+ } catch (ProvisionException e) {
+ }
+
+ try {
+ if (dbInjector != null) {
+ closeDataSource(dbInjector.getInstance(DatabaseModule.DS));
+ }
+ } catch (ConfigurationException ce) {
+ } catch (ProvisionException ce) {
+ }
+
+ super.contextDestroyed(event);
+ }
+
+ private void closeDataSource(final DataSource ds) {
+ try {
+ Class<?> type = Class.forName("org.apache.commons.dbcp.BasicDataSource");
+ if (type.isInstance(ds)) {
+ type.getMethod("close").invoke(ds);
+ return;
+ }
+ } catch (Throwable bad) {
+ // Oh well, its not a Commons DBCP pooled connection.
+ }
+
+ try {
+ Class<?> type = Class.forName("com.mchange.v2.c3p0.DataSources");
+ if (type.isInstance(ds)) {
+ type.getMethod("destroy", DataSource.class).invoke(null, ds);
+ return;
+ }
+ } catch (Throwable bad) {
+ // Oh well, its not a c3p0 pooled connection.
+ }
+ }
+}
diff --git a/src/main/java/log4j.properties b/gerrit-war/src/main/java/log4j.properties
index 183f3a8a81..183f3a8a81 100644
--- a/src/main/java/log4j.properties
+++ b/gerrit-war/src/main/java/log4j.properties
diff --git a/src/main/java/GerritServer.properties_example b/gerrit-war/src/main/webapp/WEB-INF/extra/GerritServer.properties_example
index b158fef33f..b158fef33f 100644
--- a/src/main/java/GerritServer.properties_example
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/GerritServer.properties_example
diff --git a/src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh
index f8e22d952a..f8e22d952a 100644
--- a/src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit-jetty.sh
diff --git a/src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml
index cbc434321e..cbc434321e 100644
--- a/src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/gerrit.xml
diff --git a/src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml
index 2bf77b8383..2bf77b8383 100644
--- a/src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty6/jetty_sslproxy.xml
diff --git a/src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh
index 1342d804e4..1342d804e4 100644
--- a/src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit-jetty.sh
diff --git a/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
index 117bf61fdc..117bf61fdc 100644
--- a/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
diff --git a/src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml
index 652acadeb9..652acadeb9 100644
--- a/src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/jetty_sslproxy.xml
diff --git a/src/main/webapp/WEB-INF/sql/index_generic.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/index_generic.sql
index cfdb23681e..cfdb23681e 100644
--- a/src/main/webapp/WEB-INF/sql/index_generic.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/index_generic.sql
diff --git a/src/main/webapp/WEB-INF/sql/index_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/index_postgres.sql
index b007a3b246..b007a3b246 100644
--- a/src/main/webapp/WEB-INF/sql/index_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/index_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/mysql_nextval.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/mysql_nextval.sql
index 991397a717..991397a717 100644
--- a/src/main/webapp/WEB-INF/sql/mysql_nextval.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/mysql_nextval.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade003_004.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade003_004.sql
index 3c2634fd39..3c2634fd39 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade003_004.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade003_004.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql
index f3b2ad4ddd..f3b2ad4ddd 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part1.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql
index 868e535e11..868e535e11 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade004_005_part2.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade005_006.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade005_006.sql
index f8113706c3..f8113706c3 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade005_006.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade005_006.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade006_007.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade006_007.sql
index 0187db693c..0187db693c 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade006_007.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade006_007.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade007_008.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade007_008.sql
index 40179e4071..40179e4071 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade007_008.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade007_008.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade008_009.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade008_009.sql
index 135fea6d38..135fea6d38 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade008_009.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade008_009.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade009_010.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade009_010.sql
index c3292cc5bb..c3292cc5bb 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade009_010.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade009_010.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade010_011.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade010_011.sql
index d005df6c39..d005df6c39 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade010_011.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade010_011.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql
index 5545ebc139..5545ebc139 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part1.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql
index 7083e353ad..7083e353ad 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade011_012_part2.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql
index 30a5890b4d..30a5890b4d 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql
index ad173a5da2..ad173a5da2 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade012_013_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql
index 5e1a56dd40..5e1a56dd40 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql
index 1e66639a68..1e66639a68 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade013_014_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql
index 6f68838df3..6f68838df3 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql
index 0c56f4139b..0c56f4139b 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part1_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql
index 8b728c4ff0..8b728c4ff0 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade014_015_part2.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql
index 7ca9cc80d8..7ca9cc80d8 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql
index b05f84d31e..b05f84d31e 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part1_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql
index 21fdc9cfe0..21fdc9cfe0 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade015_016_part2.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql
index 007377bcf6..007377bcf6 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql
index 66f5a7ec85..66f5a7ec85 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade016_017_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql
index b1e3b3f32b..b1e3b3f32b 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql
index 04cf4c3292..04cf4c3292 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade017_018_postgres.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql
index a0de31bd01..a0de31bd01 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_mysql.sql
diff --git a/src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql
index fbf7c81948..fbf7c81948 100644
--- a/src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql
+++ b/gerrit-war/src/main/webapp/WEB-INF/sql/upgrade018_019_postgres.sql
diff --git a/gerrit-war/src/main/webapp/WEB-INF/web.xml b/gerrit-war/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000000..205341c35a
--- /dev/null
+++ b/gerrit-war/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app>
+ <resource-ref>
+ <res-ref-name>jdbc/ReviewDb</res-ref-name>
+ <res-type>javax.sql.DataSource</res-type>
+ <res-auth>Container</res-auth>
+ </resource-ref>
+
+ <filter>
+ <filter-name>guiceFilter</filter-name>
+ <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>guiceFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+ <listener>
+ <listener-class>com.google.gerrit.httpd.WebAppInitializer</listener-class>
+ </listener>
+</web-app>
diff --git a/src/main/webapp/favicon.ico b/gerrit-war/src/main/webapp/favicon.ico
index 155217bf7d..155217bf7d 100644
--- a/src/main/webapp/favicon.ico
+++ b/gerrit-war/src/main/webapp/favicon.ico
Binary files differ
diff --git a/src/main/webapp/robots.txt b/gerrit-war/src/main/webapp/robots.txt
index c0339176af..c0339176af 100644
--- a/src/main/webapp/robots.txt
+++ b/gerrit-war/src/main/webapp/robots.txt
diff --git a/gerrit_debug.launch b/gerrit_debug.launch
deleted file mode 100644
index 077458a66d..0000000000
--- a/gerrit_debug.launch
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<stringAttribute key="bad_container_name" value="/gerrit-appja"/>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/gerrit"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
-<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gwtjsonrpc&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gwtorm&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
-<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;gerrit&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.USER_LIBRARY/GWT_17&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;gerrit&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war ${resource_loc:/gerrit/src/main/webapp} -startupUrl /Gerrit com.google.gerrit.Gerrit"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
-<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties} -Dcom.google.gerrit.server.http.BecomeAnyAccountLoginServlet=true"/>
-</launchConfiguration>
diff --git a/gerrit_macos.launch b/gerrit_macos.launch
deleted file mode 100644
index 83fbd1813f..0000000000
--- a/gerrit_macos.launch
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
-<stringAttribute key="bad_container_name" value="/gerrit-appja"/>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/gerrit"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="4"/>
-</listAttribute>
-<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
-<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gwtjsonrpc&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gwtorm&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
-<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;gerrit&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.USER_LIBRARY/GWT_17&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
-<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;gerrit&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
-<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war ${resource_loc:/gerrit/src/main/webapp} -startupUrl /Gerrit com.google.gerrit.Gerrit"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit"/>
-<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M -XstartOnFirstThread -DGerritServer=${resource_loc:/gerrit/src/main/java/GerritServer.properties} -Dcom.google.gerrit.server.http.BecomeAnyAccountLoginServlet=true"/>
-</launchConfiguration>
diff --git a/pom.xml b/pom.xml
index 8a37120216..57f1f90b58 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,30 +18,71 @@ limitations under the License.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>gerrit</groupId>
- <artifactId>gerrit</artifactId>
- <packaging>war</packaging>
+
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-parent</artifactId>
+ <packaging>pom</packaging>
<version>2.0.25-SNAPSHOT</version>
- <name>gerrit</name>
- <description>Gerrit - Web Based Code Review</description>
- <url>http://android.git.kernel.org/?p=tools/gerrit.git</url>
+
+ <name>Gerrit Code Review - Parent</name>
+ <url>http://code.google.com/p/gerrit/</url>
+
+ <description>
+ Gerrit - Web Based Code Review
+ </description>
<mailingLists>
<mailingList>
<name>repo-discuss mailing list</name>
<post>repo-discuss@googlegroups.com</post>
+ <archive>http://groups.google.com/group/repo-discuss</archive>
+ <subscribe>http://groups.google.com/group/repo-discuss/subscribe</subscribe>
</mailingList>
</mailingLists>
- <developers>
- <developer>
- <name>Shawn O. Pearce</name>
- <email>sop@google.com</email>
- <roles>
- <role>Maintainer</role>
- </roles>
- </developer>
- </developers>
+ <issueManagement>
+ <url>http://code.google.com/p/gerrit/issues/list</url>
+ <system>Google Code</system>
+ </issueManagement>
+
+ <properties>
+ <jgitVersion>0.6.0-23-g36af95b7</jgitVersion>
+ <gwtormVersion>1.1.2</gwtormVersion>
+ <gwtjsonrpcVersion>1.1.1</gwtjsonrpcVersion>
+ <gwtexpuiVersion>1.1.4-SNAPSHOT</gwtexpuiVersion>
+ <gwtVersion>1.7.0</gwtVersion>
+ <slf4jVersion>1.5.8</slf4jVersion>
+ <guiceVersion>2.0</guiceVersion>
+
+ <gwtStyle>OBF</gwtStyle>
+
+ <project.build.sourceEncoding>
+ UTF-8
+ </project.build.sourceEncoding>
+ <project.reporting.outputEncoding>
+ UTF-8
+ </project.reporting.outputEncoding>
+ </properties>
+
+ <modules>
+ <module>gerrit-patch-commonsnet</module>
+ <module>gerrit-patch-gwtexpui</module>
+ <module>gerrit-patch-jgit</module>
+
+ <module>gerrit-util-cli</module>
+ <module>gerrit-util-ssl</module>
+
+ <module>gerrit-common</module>
+ <module>gerrit-httpd</module>
+ <module>gerrit-main</module>
+ <module>gerrit-pgm</module>
+ <module>gerrit-reviewdb</module>
+ <module>gerrit-server</module>
+ <module>gerrit-sshd</module>
+ <module>gerrit-war</module>
+
+ <module>gerrit-gwtui</module>
+ </modules>
<licenses>
<license>
@@ -252,203 +293,34 @@ limitations under the License.
</license>
</licenses>
- <properties>
- <jgitVersion>0.6.0-23-g36af95b7</jgitVersion>
- <gwtormVersion>1.1.2</gwtormVersion>
- <gwtjsonrpcVersion>1.1.1</gwtjsonrpcVersion>
- <gwtexpuiVersion>1.1.4-SNAPSHOT</gwtexpuiVersion>
- <gwtVersion>1.7.0</gwtVersion>
- <gwtStyle>OBF</gwtStyle>
- <project.build.sourceEncoding>
- UTF-8
- </project.build.sourceEncoding>
- <project.reporting.outputEncoding>
- UTF-8
- </project.reporting.outputEncoding>
- </properties>
-
<build>
<resources>
<resource>
- <directory>src/main/java</directory>
- <includes>
- <include>log4j.properties</include>
- <include>com/google/gerrit/client/GerritVersion.properties</include>
- <include>com/google/gerrit/server/ssh/scproot/**</include>
- </includes>
+ <directory>${basedir}/src/main/java</directory>
+ <excludes>
+ <exclude>**/*.gwt.xml</exclude>
+ <exclude>**/*.java</exclude>
+ </excludes>
</resource>
</resources>
<plugins>
<plugin>
- <artifactId>maven-clean-plugin</artifactId>
- <version>2.2</version>
- <configuration>
- <filesets>
- <fileset>
- <directory>src/main/webapp</directory>
- <includes>
- <include>gerrit</include>
- <include>WEB-INF/lib</include>
- </includes>
- <followSymlinks>false</followSymlinks>
- </fileset>
- </filesets>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.antlr</groupId>
- <artifactId>antlr3-maven-plugin</artifactId>
- <version>3.1.1</version>
- <executions>
- <execution>
- <goals>
- <goal>antlr</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
+ <encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>gwt-maven-plugin</artifactId>
- <version>1.1</version>
- <configuration>
- <module>com.google.gerrit.Gerrit</module>
- <output>${project.build.directory}/${project.name}-${project.version}</output>
- <extraJvmArgs>-Xmx512m</extraJvmArgs>
- <style>${gwtStyle}</style>
- </configuration>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-war-plugin</artifactId>
- <configuration>
- <warSourceExcludes>WEB-INF/web-jetty.xml</warSourceExcludes>
- <archiveClasses>true</archiveClasses>
- <archive>
- <manifest>
- <mainClass>ExecutableWarMain</mainClass>
- </manifest>
- <manifestEntries>
- <Executable-War-Package>com.google.gerrit.pgm</Executable-War-Package>
- </manifestEntries>
- </archive>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-dependency-plugin</artifactId>
+ <artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
- <id>make-executable</id>
- <phase>generate-resources</phase>
<goals>
- <goal>unpack</goal>
- </goals>
- <configuration>
- <artifactItems>
- <artifactItem>
- <groupId>gerrit</groupId>
- <artifactId>executablewar</artifactId>
- <overWrite>true</overWrite>
- <outputDirectory>${project.build.directory}/executablewar</outputDirectory>
- <includes>**/*.class</includes>
- </artifactItem>
- <artifactItem>
- <groupId>gerrit</groupId>
- <artifactId>gerrit-keyapplet</artifactId>
- <overWrite>true</overWrite>
- <outputDirectory>${project.build.directory}/gerrit-keyapplet</outputDirectory>
- <includes>**/*</includes>
- </artifactItem>
- </artifactItems>
- </configuration>
- </execution>
- </executions>
- </plugin>
-
- <plugin>
- <artifactId>maven-antrun-plugin</artifactId>
- <executions>
- <execution>
- <id>generate-version</id>
- <phase>generate-resources</phase>
- <configuration>
- <tasks>
- <property name="s" location="${basedir}/src/main/java/"/>
- <exec executable="git" outputproperty="v">
- <arg value="describe"/>
- <arg value="HEAD"/>
- </exec>
- <echo file="${s}/com/google/gerrit/client/GerritVersion.properties">version=${v}</echo>
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
-
- <execution>
- <id>fix-output</id>
- <phase>process-classes</phase>
- <configuration>
- <tasks>
- <property name="d" location="${basedir}/target/${project.name}-${project.version}"/>
- <property name="m" location="${d}/gerrit"/>
- <property name="keyapplet" location="${basedir}/target/gerrit-keyapplet"/>
-
- <copy todir="${d}">
- <fileset dir="${basedir}/target/executablewar" includes="**/*"/>
- </copy>
-
- <copy tofile="${d}/LICENSES.txt"
- file="${basedir}/Documentation/licenses.txt"
- overwrite="true"/>
- <copy todir="${d}/WEB-INF/extra">
- <fileset dir="${basedir}/src/main/java">
- <include name="GerritServer.properties_example"/>
- </fileset>
- </copy>
-
- <apply executable="gzip" addsourcefile="false">
- <arg value="-9"/>
- <fileset dir="${m}"
- includes="**/*.html,**/*.css"/>
- <redirector>
- <inputmapper type="glob" from="*" to="${m}/*"/>
- <outputmapper type="glob" from="*" to="${m}/*.gz"/>
- </redirector>
- </apply>
-
- <zip
- destfile="${m}/gerrit-keyapplet.cache.jar"
- compress="true">
- <fileset dir="${keyapplet}" includes="**/*"/>
- </zip>
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
+ <goal>jar</goal>
</goals>
</execution>
</executions>
@@ -458,232 +330,439 @@ limitations under the License.
<dependencies>
<dependency>
- <groupId>gwtorm</groupId>
- <artifactId>gwtorm</artifactId>
- <version>${gwtormVersion}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>gwtorm</groupId>
- <artifactId>gwtorm</artifactId>
- <version>${gwtormVersion}</version>
- <classifier>sources</classifier>
- <scope>provided</scope>
- <type>jar</type>
- </dependency>
-
- <dependency>
- <groupId>gwtjsonrpc</groupId>
- <artifactId>gwtjsonrpc</artifactId>
- <version>${gwtjsonrpcVersion}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>gwtjsonrpc</groupId>
- <artifactId>gwtjsonrpc</artifactId>
- <version>${gwtjsonrpcVersion}</version>
- <classifier>sources</classifier>
- <scope>provided</scope>
- <type>jar</type>
- </dependency>
-
- <dependency>
- <groupId>gwtexpui</groupId>
- <artifactId>gwtexpui</artifactId>
- <version>${gwtexpuiVersion}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>gwtexpui</groupId>
- <artifactId>gwtexpui</artifactId>
- <version>${gwtexpuiVersion}</version>
- <classifier>sources</classifier>
- <scope>provided</scope>
- <type>jar</type>
- </dependency>
-
- <dependency>
- <groupId>gerrit</groupId>
- <artifactId>executablewar</artifactId>
- <version>1.2</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>gerrit</groupId>
- <artifactId>gerrit-keyapplet</artifactId>
- <version>1.0</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.openid4java</groupId>
- <artifactId>openid4java-consumer</artifactId>
- <version>0.9.5</version>
- <scope>compile</scope>
- <exclusions>
- <exclusion>
- <!-- jug-1.1 is LGPL, and the source has been lost -->
- <groupId>jug</groupId>
- <artifactId>jug</artifactId>
- </exclusion>
- <exclusion>
- <!-- not required on Java 5 or later -->
- <groupId>xml-apis</groupId>
- <artifactId>xml-apis</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.apache.sshd</groupId>
- <artifactId>sshd-core</artifactId>
- <version>0.2.0</version>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache-core</artifactId>
- <version>1.7.0</version>
- </dependency>
-
- <dependency>
- <groupId>args4j</groupId>
- <artifactId>args4j</artifactId>
- <version>2.0.16</version>
- </dependency>
-
- <dependency>
- <groupId>com.google.code.guice</groupId>
- <artifactId>guice</artifactId>
- <version>2.0</version>
- </dependency>
- <dependency>
- <groupId>com.google.code.guice</groupId>
- <artifactId>guice-servlet</artifactId>
- <version>2.0</version>
- </dependency>
- <dependency>
- <groupId>com.google.code.guice</groupId>
- <artifactId>guice-assistedinject</artifactId>
- <version>2.0</version>
- </dependency>
- <dependency>
- <!-- required by Guice (whose POM is fake and lacks it) -->
- <groupId>aopalliance</groupId>
- <artifactId>aopalliance</artifactId>
- <version>1.0</version>
- </dependency>
-
- <dependency>
- <groupId>commons-net</groupId>
- <artifactId>commons-net</artifactId>
- <version>2.0</version>
- </dependency>
-
- <dependency>
- <groupId>eu.medsea.mimeutil</groupId>
- <artifactId>mime-util</artifactId>
- <version>2.1.2</version>
- </dependency>
-
- <dependency>
- <groupId>org.antlr</groupId>
- <artifactId>antlr</artifactId>
- <version>3.1.1</version>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
- <groupId>bouncycastle</groupId>
- <artifactId>bcpg-jdk15</artifactId>
- <version>140</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.5.8</version>
- <scope>runtime</scope>
- <exclusions>
- <exclusion>
- <groupId>javax.mail</groupId>
- <artifactId>mail</artifactId>
- </exclusion>
- <exclusion>
- <groupId>javax.jms</groupId>
- <artifactId>jms</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.sun.jdmk</groupId>
- <artifactId>jmxtools</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.sun.jmx</groupId>
- <artifactId>jmxri</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
- <dependency>
- <groupId>org.eclipse</groupId>
- <artifactId>jgit</artifactId>
- <version>${jgitVersion}</version>
- <scope>compile</scope>
- </dependency>
- <dependency>
- <groupId>org.eclipse</groupId>
- <artifactId>jgit</artifactId>
- <version>${jgitVersion}</version>
- <classifier>sources</classifier>
- <scope>provided</scope>
- <type>jar</type>
- </dependency>
-
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>3.8.2</version>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <version>1.2.122</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
- <version>2.5.1</version>
<scope>test</scope>
</dependency>
+
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
- <version>2.4</version>
<scope>test</scope>
</dependency>
-
- <!-- GWT -->
- <dependency>
- <groupId>com.google.gwt</groupId>
- <artifactId>gwt-servlet</artifactId>
- <version>${gwtVersion}</version>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>com.google.gwt</groupId>
- <artifactId>gwt-user</artifactId>
- <version>${gwtVersion}</version>
- <scope>provided</scope>
- </dependency>
</dependencies>
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-commonsnet</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-gwtexpui</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-gwtexpui</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-jgit</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-patch-jgit</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-common</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-gwtui</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-gwtui</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-httpd</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-main</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-pgm</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-reviewdb</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-reviewdb</artifactId>
+ <version>${project.version}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-sshd</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-war</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-war</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gerrit</groupId>
+ <artifactId>gerrit-util-ssl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtorm</groupId>
+ <artifactId>gwtorm</artifactId>
+ <version>${gwtormVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>gwtorm</groupId>
+ <artifactId>gwtorm</artifactId>
+ <version>${gwtormVersion}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ <version>${gwtjsonrpcVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>gwtjsonrpc</groupId>
+ <artifactId>gwtjsonrpc</artifactId>
+ <version>${gwtjsonrpcVersion}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ <version>${gwtexpuiVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>gwtexpui</groupId>
+ <artifactId>gwtexpui</artifactId>
+ <version>${gwtexpuiVersion}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>gerrit</groupId>
+ <artifactId>gerrit-keyapplet</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.openid4java</groupId>
+ <artifactId>openid4java-consumer</artifactId>
+ <version>0.9.5</version>
+ <exclusions>
+ <exclusion>
+ <!-- jug-1.1 is LGPL, and the source has been lost -->
+ <groupId>jug</groupId>
+ <artifactId>jug</artifactId>
+ </exclusion>
+ <exclusion>
+ <!-- not required on Java 5 or later -->
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>0.2.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache-core</artifactId>
+ <version>1.7.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>args4j</groupId>
+ <artifactId>args4j</artifactId>
+ <version>2.0.16</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice</artifactId>
+ <version>${guiceVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice-servlet</artifactId>
+ <version>${guiceVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.code.guice</groupId>
+ <artifactId>guice-assistedinject</artifactId>
+ <version>${guiceVersion}</version>
+ </dependency>
+
+ <dependency>
+ <!-- required by Guice (whose POM is fake and lacks it) -->
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-net</groupId>
+ <artifactId>commons-net</artifactId>
+ <version>2.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>1.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>eu.medsea.mimeutil</groupId>
+ <artifactId>mime-util</artifactId>
+ <version>2.1.2</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr</artifactId>
+ <version>3.1.1</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.antlr</groupId>
+ <artifactId>stringtemplate</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>antlr</groupId>
+ <artifactId>antlr</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcpg-jdk15</artifactId>
+ <version>140</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4jVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4jVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.15</version>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javax.jms</groupId>
+ <artifactId>jms</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.jdmk</groupId>
+ <artifactId>jmxtools</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.jmx</groupId>
+ <artifactId>jmxri</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ <version>${jgitVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>jgit</artifactId>
+ <version>${jgitVersion}</version>
+ <classifier>sources</classifier>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <version>1.2.122</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>2.5.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>2.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-servlet</artifactId>
+ <version>${gwtVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <version>${gwtVersion}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-dev</artifactId>
+ <version>${gwtVersion}</version>
+ <classifier>${platform}</classifier>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <profiles>
+ <profile>
+ <id>gwt-dev-windows</id>
+ <properties>
+ <platform>windows</platform>
+ </properties>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ <os>
+ <family>windows</family>
+ </os>
+ </activation>
+ </profile>
+
+ <profile>
+ <id>gwt-dev-mac</id>
+ <properties>
+ <platform>mac</platform>
+ </properties>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ <os>
+ <family>mac</family>
+ </os>
+ </activation>
+ </profile>
+
+ <profile>
+ <id>gwt-dev-linux</id>
+ <properties>
+ <platform>linux</platform>
+ </properties>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ <os>
+ <name>linux</name>
+ </os>
+ </activation>
+ </profile>
+ </profiles>
+
<repositories>
<repository>
<id>jgit-repository</id>
diff --git a/src/main/java/com/google/gerrit/Gerrit.gwt.xml b/src/main/java/com/google/gerrit/Gerrit.gwt.xml
deleted file mode 100644
index ebb833fb23..0000000000
--- a/src/main/java/com/google/gerrit/Gerrit.gwt.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
- Copyright (C) 2008 The Android Open Source Project
-
- 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.
--->
-<module rename-to="gerrit">
- <inherits name='com.google.gwt.user.User'/>
- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
- <inherits name='com.google.gwtjsonrpc.GWTJSONRPC'/>
- <inherits name='com.google.gwtorm.GWTORM'/>
- <inherits name='com.google.gwtexpui.css.CSS'/>
- <inherits name='com.google.gwtexpui.clippy.Clippy'/>
- <inherits name='com.google.gwtexpui.globalkey.GlobalKey'/>
- <inherits name='com.google.gwtexpui.progress.Progress'/>
- <inherits name='com.google.gwtexpui.safehtml.SafeHtml'/>
- <inherits name='com.google.gerrit.UserAgent'/>
- <inherits name='org.eclipse.jgit.JGit'/>
- <stylesheet src='gerrit.css' />
- <stylesheet src='prettify20090521/prettify.css' />
- <extend-property name="locale" values="en"/>
-
- <entry-point class='com.google.gerrit.client.Gerrit'/>
-</module>
diff --git a/src/main/java/com/google/gerrit/client/FormatUtil.java b/src/main/java/com/google/gerrit/client/FormatUtil.java
deleted file mode 100644
index d1b21d9ac3..0000000000
--- a/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gwt.i18n.client.DateTimeFormat;
-
-import java.util.Date;
-
-/** Misc. formatting functions. */
-public class FormatUtil {
- private static final long ONE_YEAR = 182L * 24 * 60 * 60 * 1000;
-
- private static final DateTimeFormat sTime =
- DateTimeFormat.getShortTimeFormat();
- private static final DateTimeFormat sDate = DateTimeFormat.getFormat("MMM d");
- private static final DateTimeFormat mDate =
- DateTimeFormat.getMediumDateFormat();
- private static final DateTimeFormat dtfmt =
- DateTimeFormat.getFormat(mDate.getPattern() + " " + sTime.getPattern());
-
- /** Format a date using a really short format. */
- public static String shortFormat(Date dt) {
- if (dt == null) {
- return "";
- }
-
- final Date now = new Date();
- dt = new Date(dt.getTime());
- if (mDate.format(now).equals(mDate.format(dt))) {
- // Same day as today, report only the time.
- //
- return sTime.format(dt);
-
- } else if (Math.abs(now.getTime() - dt.getTime()) < ONE_YEAR) {
- // Within the last year, show a shorter date.
- //
- return sDate.format(dt);
-
- } else {
- // Report only date and year, its far away from now.
- //
- return mDate.format(dt);
- }
- }
-
- /** Format a date using the locale's medium length format. */
- public static String mediumFormat(final Date dt) {
- if (dt == null) {
- return "";
- }
- return dtfmt.format(new Date(dt.getTime()));
- }
-
- /** Format an account as a name and email address. */
- public static String nameEmail(final Account acct) {
- return nameEmail(new AccountInfo(acct));
- }
-
- /**
- * Formats an account as an name and an email address.
- * <p>
- * Example output:
- * <ul>
- * <li><code>A U. Thor &lt;author@example.com&gt;</code>: full populated</li>
- * <li><code>A U. Thor (12)</code>: missing email address</li>
- * <li><code>Anonymous Coward &lt;author@example.com&gt;</code>: missing name</li>
- * <li><code>Anonymous Coward (12)</code>: missing name and email address</li>
- * </ul>
- */
- public static String nameEmail(final AccountInfo acct) {
- String name = acct.getFullName();
- if (name == null) {
- name = Gerrit.C.anonymousCoward();
- }
-
- final StringBuilder b = new StringBuilder();
- b.append(name);
- if (acct.getPreferredEmail() != null) {
- b.append(" <");
- b.append(acct.getPreferredEmail());
- b.append(">");
- } else if (acct.getId() != null) {
- b.append(" (");
- b.append(acct.getId().get());
- b.append(")");
- }
- return b.toString();
- }
-
- /**
- * Formats an account name.
- * <p>
- * If the account has a full name, it returns only the full name. Otherwise it
- * returns a longer form that includes the email address.
- */
- public static String name(final AccountInfo ai) {
- if (ai.getFullName() != null) {
- return ai.getFullName();
- }
- if (ai.getPreferredEmail() != null) {
- return ai.getPreferredEmail();
- }
- return nameEmail(ai);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/Gerrit.java b/src/main/java/com/google/gerrit/client/Gerrit.java
deleted file mode 100644
index 6b655b846d..0000000000
--- a/src/main/java/com/google/gerrit/client/Gerrit.java
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
-import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.client.data.SystemInfoService;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.LinkMenuBar;
-import com.google.gerrit.client.ui.LinkMenuItem;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.core.client.EntryPoint;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Cookies;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.Accessibility;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.TabPanel;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-import com.google.gwtexpui.user.client.UserAgent;
-import com.google.gwtexpui.user.client.ViewSite;
-import com.google.gwtjsonrpc.client.JsonDefTarget;
-import com.google.gwtjsonrpc.client.JsonUtil;
-import com.google.gwtjsonrpc.client.XsrfManager;
-
-import java.util.ArrayList;
-
-public class Gerrit implements EntryPoint {
- public static final GerritConstants C = GWT.create(GerritConstants.class);
- public static final GerritMessages M = GWT.create(GerritMessages.class);
- public static final GerritIcons ICONS = GWT.create(GerritIcons.class);
- public static final SystemInfoService SYSTEM_SVC;
-
- private static String myHost;
- private static String myVersion;
- private static GerritConfig myConfig;
- private static Account myAccount;
-
- private static TabPanel menuLeft;
- private static LinkMenuBar menuRight;
- private static RootPanel siteHeader;
- private static RootPanel siteFooter;
- private static SearchPanel searchPanel;
- private static ViewSite<Screen> body;
-
- static {
- SYSTEM_SVC = GWT.create(SystemInfoService.class);
- JsonUtil.bind(SYSTEM_SVC, "rpc/SystemInfoService");
- }
-
- public static void display(final String historyToken, final boolean go) {
- History.newItem(historyToken, go);
- if (!go && historyHooks != null) {
- dispatchHistoryHooks(historyToken);
- }
- }
-
- public static void display(final String historyToken, final Screen view) {
- History.newItem(historyToken, false);
- display(view);
- if (historyHooks != null) {
- dispatchHistoryHooks(historyToken);
- }
- }
-
- public static void display(final Screen view) {
- if (view.isRequiresSignIn() && !isSignedIn()) {
- doSignIn();
- } else {
- body.setView(view);
- }
- }
-
- public static void setQueryString(String query) {
- searchPanel.setText(query);
- }
-
- public static void setWindowTitle(final Screen screen, final String text) {
- if (screen == body.getView()) {
- if (text == null || text.length() == 0) {
- Window.setTitle(M.windowTitle1(myHost));
- } else {
- Window.setTitle(M.windowTitle2(text, myHost));
- }
- }
- }
-
- /** Get the public configuration data used by this Gerrit instance. */
- public static GerritConfig getConfig() {
- return myConfig;
- }
-
- /** @return the currently signed in user's account data; null if no account */
- public static Account getUserAccount() {
- return myAccount;
- }
-
- /** @return true if the user is currently authenticated */
- public static boolean isSignedIn() {
- return getUserAccount() != null;
- }
-
- /** Sign the user into the application. */
- public static void doSignIn() {
- switch (myConfig.getAuthType()) {
- case HTTP:
- case HTTP_LDAP:
- Location.assign(Location.getPath() + "login/" + History.getToken());
- break;
-
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- Location.assign(Location.getPath() + "become");
- break;
-
- case OPENID:
- new OpenIdSignInDialog(SignInDialog.Mode.SIGN_IN, null).center();
- break;
-
- case LDAP:
- new UserPassSignInDialog(null).center();
- break;
- }
- }
-
- public void onModuleLoad() {
- UserAgent.assertNotInIFrame();
- initHostname();
- Window.setTitle(M.windowTitle1(myHost));
- initHistoryHooks();
- populateBottomMenu();
-
- final RootPanel menuArea = RootPanel.get("gerrit_topmenu");
- final Grid menuLine = new Grid(1, 3);
- menuLeft = new TabPanel();
- menuRight = new LinkMenuBar();
- searchPanel = new SearchPanel();
- menuLeft.setStyleName("gerrit-topmenu-menuLeft");
- menuLine.setStyleName("gerrit-topmenu");
- menuArea.add(menuLine);
- final FlowPanel menuRightPanel = new FlowPanel();
- menuRightPanel.setStyleName("gerrit-topmenu-menuRight");
- menuRightPanel.add(menuRight);
- menuRightPanel.add(searchPanel);
- menuLine.setWidget(0, 0, menuLeft);
- menuLine.setWidget(0, 1, new FlowPanel());
- menuLine.setWidget(0, 2, menuRightPanel);
- final CellFormatter fmt = menuLine.getCellFormatter();
- fmt.setStyleName(0, 0, "gerrit-topmenu-TDmenu");
- fmt.setStyleName(0, 1, "gerrit-topmenu-TDglue");
- fmt.setStyleName(0, 2, "gerrit-topmenu-TDmenu");
-
- siteHeader = RootPanel.get("gerrit_header");
- siteFooter = RootPanel.get("gerrit_footer");
-
- body = new ViewSite<Screen>() {
- @Override
- protected void onShowView(Screen view) {
- super.onShowView(view);
- view.onShowView();
- }
- };
- RootPanel.get("gerrit_body").add(body);
-
- final RpcStatus rpcStatus = new RpcStatus(menuArea);
- JsonUtil.addRpcStartHandler(rpcStatus);
- JsonUtil.addRpcCompleteHandler(rpcStatus);
- JsonUtil.setDefaultXsrfManager(new XsrfManager() {
- @Override
- public String getToken(JsonDefTarget proxy) {
- return Cookies.getCookie("GerritAccount");
- }
-
- @Override
- public void setToken(JsonDefTarget proxy, String token) {
- // Ignore the request, we always rely upon the cookie.
- }
- });
-
- final HostPageDataService hpd = GWT.create(HostPageDataService.class);
- hpd.load(new GerritCallback<HostPageData>() {
- public void onSuccess(final HostPageData result) {
- myConfig = result.config;
- if (result.userAccount != null) {
- myAccount = result.userAccount;
- applyUserPreferences();
- }
- onModuleLoad2();
- }
- });
- }
-
- private static void initHostname() {
- myHost = Location.getHostName();
- final int d1 = myHost.indexOf('.');
- if (d1 < 0) {
- return;
- }
- final int d2 = myHost.indexOf('.', d1 + 1);
- if (d2 >= 0) {
- myHost = myHost.substring(0, d2);
- }
- }
-
- private static ArrayList<JavaScriptObject> historyHooks;
-
- private static native void initHistoryHooks()
- /*-{ $wnd['gerrit_addHistoryHook'] = function(h) { @com.google.gerrit.client.Gerrit::addHistoryHook(Lcom/google/gwt/core/client/JavaScriptObject;)(h); }; }-*/;
-
- static void addHistoryHook(final JavaScriptObject hook) {
- if (historyHooks == null) {
- historyHooks = new ArrayList<JavaScriptObject>();
- History.addValueChangeHandler(new ValueChangeHandler<String>() {
- @Override
- public void onValueChange(ValueChangeEvent<String> event) {
- dispatchHistoryHooks(event.getValue());
- }
- });
- }
- historyHooks.add(hook);
- }
-
- private static native void callHistoryHook(JavaScriptObject hook, String url)
- /*-{ hook(url); }-*/;
-
- private static void dispatchHistoryHooks(final String historyToken) {
- final String url = Location.getPath() + "#" + historyToken;
- for (final JavaScriptObject hook : historyHooks) {
- callHistoryHook(hook, url);
- }
- }
-
- private static void populateBottomMenu() {
- final RootPanel btmmenu = RootPanel.get("gerrit_btmmenu");
-
- final Label keyHelp = new Label(C.keyHelp());
- keyHelp.setStyleName("gerrit-keyhelp");
- btmmenu.add(keyHelp);
-
- final String vs = getVersion();
- final HTML version = new HTML(M.poweredBy(vs));
- version.setStyleName("gerrit-version");
- btmmenu.add(version);
- }
-
- /** @return version number of the Gerrit application software */
- public static String getVersion() {
- if (myVersion == null) {
- if (GWT.isScript()) {
- final GerritVersion v = GWT.create(GerritVersion.class);
- myVersion = v.version();
- } else {
- myVersion = "dev";
- }
- }
- return myVersion;
- }
-
- private void onModuleLoad2() {
-
- refreshMenuBar();
-
- final RootPanel sg = RootPanel.get("gerrit_startinggerrit");
- sg.getElement().getParentElement().removeChild(sg.getElement());
- RootPanel.detachNow(sg);
-
- History.addValueChangeHandler(new Link());
- JumpKeys.register(body);
-
- if ("".equals(History.getToken())) {
- if (isSignedIn()) {
- History.newItem(Link.MINE);
- } else {
- History.newItem(Link.ALL_OPEN);
- }
- } else {
- History.fireCurrentHistoryState();
- }
- }
-
- public static void refreshMenuBar() {
- menuLeft.clear();
- menuRight.clear();
-
- final boolean signedIn = isSignedIn();
- LinkMenuBar m;
-
- m = new LinkMenuBar();
- addLink(m, C.menuAllOpen(), Link.ALL_OPEN);
- addLink(m, C.menuAllMerged(), Link.ALL_MERGED);
- addLink(m, C.menuAllAbandoned(), Link.ALL_ABANDONED);
- menuLeft.add(m, C.menuAll());
-
- if (signedIn) {
- m = new LinkMenuBar();
- addLink(m, C.menuMyChanges(), Link.MINE);
- addLink(m, C.menyMyDrafts(), Link.MINE_DRAFTS);
- addLink(m, C.menuMyStarredChanges(), Link.MINE_STARRED);
- menuLeft.add(m, C.menuMine());
- menuLeft.selectTab(1);
- } else {
- menuLeft.selectTab(0);
- }
-
- if (signedIn) {
- m = new LinkMenuBar();
- addLink(m, C.menuGroups(), Link.ADMIN_GROUPS);
- addLink(m, C.menuProjects(), Link.ADMIN_PROJECTS);
- menuLeft.add(m, C.menuAdmin());
- }
-
- if (signedIn) {
- whoAmI();
- addLink(menuRight, C.menuSettings(), Link.SETTINGS);
- menuRight.add(anchor(C.menuSignOut(), "logout"));
- } else {
- switch (getConfig().getAuthType()) {
- case HTTP:
- case HTTP_LDAP:
- break;
-
- case OPENID:
- menuRight.addItem(C.menuRegister(), new Command() {
- public void execute() {
- new OpenIdSignInDialog(SignInDialog.Mode.REGISTER, null).center();
- }
- });
- // fall through
- case LDAP:
- menuRight.addItem(C.menuSignIn(), new Command() {
- public void execute() {
- doSignIn();
- }
- });
- break;
-
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- menuRight.add(anchor("Become", "become"));
- break;
- }
- }
- }
-
- public static void applyUserPreferences() {
- final AccountGeneralPreferences p = myAccount.getGeneralPreferences();
- CopyableLabel.setFlashEnabled(p.isUseFlashClipboard());
- if (siteHeader != null) {
- siteHeader.setVisible(p.isShowSiteHeader());
- }
- if (siteFooter != null) {
- siteFooter.setVisible(p.isShowSiteHeader());
- }
- }
-
- private static void whoAmI() {
- final String name = FormatUtil.nameEmail(getUserAccount());
- final InlineLabel l = new InlineLabel(name);
- l.setStyleName("gerrit-MenuBarUserName");
- menuRight.add(l);
- }
-
- private static Anchor anchor(final String text, final String to) {
- final Anchor a = new Anchor(text, to);
- a.setStyleName("gerrit-MenuItem");
- Accessibility.setRole(a.getElement(), Accessibility.ROLE_MENUITEM);
- return a;
- }
-
- private static void addLink(final LinkMenuBar m, final String text,
- final String historyToken) {
- m.addItem(new LinkMenuItem(text, historyToken));
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/GerritVersion.java b/src/main/java/com/google/gerrit/client/GerritVersion.java
deleted file mode 100644
index f49dfd313d..0000000000
--- a/src/main/java/com/google/gerrit/client/GerritVersion.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gwt.i18n.client.Constants;
-
-/**
- * Automatically generated version code.
- * <p>
- * Do not translate this constants interface.
- */
-public interface GerritVersion extends Constants {
- String version();
-}
diff --git a/src/main/java/com/google/gerrit/client/HostPageData.java b/src/main/java/com/google/gerrit/client/HostPageData.java
deleted file mode 100644
index 14c6e7623f..0000000000
--- a/src/main/java/com/google/gerrit/client/HostPageData.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Data sent as part of the host page, to bootstrap the UI. */
-public class HostPageData {
- public Account userAccount;
- public GerritConfig config;
-}
diff --git a/src/main/java/com/google/gerrit/client/HostPageDataService.java b/src/main/java/com/google/gerrit/client/HostPageDataService.java
deleted file mode 100644
index 3553d9fe37..0000000000
--- a/src/main/java/com/google/gerrit/client/HostPageDataService.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.HostPageCache;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-interface HostPageDataService extends RemoteJsonService {
- @HostPageCache(name = "gerrit_hostpagedata_obj", once = true)
- void load(AsyncCallback<HostPageData> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/JumpKeys.java b/src/main/java/com/google/gerrit/client/JumpKeys.java
deleted file mode 100644
index ebd50686db..0000000000
--- a/src/main/java/com/google/gerrit/client/JumpKeys.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.CompoundKeyCommand;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-
-class JumpKeys {
- static void register(final Widget body) {
- final KeyCommandSet jumps = new KeyCommandSet();
-
- jumps.add(new KeyCommand(0, 'o', Gerrit.C.jumpAllOpen()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(Link.ALL_OPEN, true);
- }
- });
- jumps.add(new KeyCommand(0, 'm', Gerrit.C.jumpAllMerged()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(Link.ALL_MERGED, true);
- }
- });
-
- if (Gerrit.isSignedIn()) {
- jumps.add(new KeyCommand(0, 'i', Gerrit.C.jumpMine()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(Link.MINE, true);
- }
- });
- jumps.add(new KeyCommand(0, 'd', Gerrit.C.jumpMineDrafts()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(Link.MINE_DRAFTS, true);
- }
- });
- jumps.add(new KeyCommand(0, 's', Gerrit.C.jumpMineStarred()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display(Link.MINE_STARRED, true);
- }
- });
- }
-
- final KeyCommandSet jumping = new KeyCommandSet(Gerrit.C.sectionJumping());
- jumping.add(new CompoundKeyCommand(0, 'g', "", jumps));
- GlobalKey.add(body, jumping);
- }
-
- private JumpKeys() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/Link.java b/src/main/java/com/google/gerrit/client/Link.java
deleted file mode 100644
index d5fda50a01..0000000000
--- a/src/main/java/com/google/gerrit/client/Link.java
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gerrit.client.account.AccountSettings;
-import com.google.gerrit.client.account.NewAgreementScreen;
-import com.google.gerrit.client.account.RegisterScreen;
-import com.google.gerrit.client.account.ValidateEmailScreen;
-import com.google.gerrit.client.admin.AccountGroupScreen;
-import com.google.gerrit.client.admin.GroupListScreen;
-import com.google.gerrit.client.admin.ProjectAdminScreen;
-import com.google.gerrit.client.admin.ProjectListScreen;
-import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
-import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
-import com.google.gerrit.client.changes.AccountDashboardScreen;
-import com.google.gerrit.client.changes.AllAbandonedChangesScreen;
-import com.google.gerrit.client.changes.AllMergedChangesScreen;
-import com.google.gerrit.client.changes.AllOpenChangesScreen;
-import com.google.gerrit.client.changes.ByProjectAbandonedChangesScreen;
-import com.google.gerrit.client.changes.ByProjectMergedChangesScreen;
-import com.google.gerrit.client.changes.ByProjectOpenChangesScreen;
-import com.google.gerrit.client.changes.ChangeQueryResultsScreen;
-import com.google.gerrit.client.changes.ChangeScreen;
-import com.google.gerrit.client.changes.MineDraftsScreen;
-import com.google.gerrit.client.changes.MineStarredScreen;
-import com.google.gerrit.client.changes.PublishCommentScreen;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.patches.PatchScreen;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.Change.Status;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwtorm.client.KeyUtil;
-
-public class Link implements ValueChangeHandler<String> {
- public static final String SETTINGS = "settings";
- public static final String SETTINGS_SSHKEYS = "settings,ssh-keys";
- public static final String SETTINGS_WEBIDENT = "settings,web-identities";
- public static final String SETTINGS_MYGROUPS = "settings,group-memberships";
- public static final String SETTINGS_AGREEMENTS = "settings,agreements";
- public static final String SETTINGS_CONTACT = "settings,contact";
- public static final String SETTINGS_PROJECTS = "settings,projects";
- public static final String SETTINGS_NEW_AGREEMENT = "settings,new-agreement";
- public static final String REGISTER = "register";
-
- public static final String MINE = "mine";
- public static final String MINE_STARRED = "mine,starred";
- public static final String MINE_DRAFTS = "mine,drafts";
-
- public static final String ALL_ABANDONED = "all,abandoned,n,z";
- public static final String ALL_MERGED = "all,merged,n,z";
- public static final String ALL_OPEN = "all,open,n,z";
-
- public static final String ADMIN_PEOPLE = "admin,people";
- public static final String ADMIN_GROUPS = "admin,groups";
- public static final String ADMIN_PROJECTS = "admin,projects";
-
- public static String toChange(final ChangeInfo c) {
- return toChange(c.getId());
- }
-
- public static String toChange(final Change.Id c) {
- return "change," + c.toString();
- }
-
- public static String toAccountDashboard(final AccountInfo acct) {
- return toAccountDashboard(acct.getId());
- }
-
- public static String toAccountDashboard(final Account.Id acct) {
- return "dashboard," + acct.toString();
- }
-
- public static String toPatchSideBySide(final Patch.Key id) {
- return toPatch("sidebyside", id);
- }
-
- public static String toPatchUnified(final Patch.Key id) {
- return toPatch("unified", id);
- }
-
- public static String toPatch(final String type, final Patch.Key id) {
- return "patch," + type + "," + id.toString();
- }
-
- public static String toAccountGroup(final AccountGroup.Id id) {
- return "admin,group," + id.toString();
- }
-
- public static String toProjectAdmin(final Project.NameKey n, final String tab) {
- return "admin,project," + n.toString() + "," + tab;
- }
-
- public static String toProject(final Project.NameKey proj, Status status) {
- switch (status) {
- case ABANDONED:
- return "project,abandoned," + proj.toString() + ",n,z";
-
- case MERGED:
- return "project,merged," + proj.toString() + ",n,z";
-
- case NEW:
- case SUBMITTED:
- default:
- return "project,open," + proj.toString() + ",n,z";
- }
- }
-
- public static String toChangeQuery(final String query) {
- return "q," + KeyUtil.encode(query) + ",n,z";
- }
-
- @Override
- public void onValueChange(final ValueChangeEvent<String> event) {
- final String token = event.getValue();
- Screen s;
- try {
- s = select(token);
- } catch (RuntimeException err) {
- GWT.log("Error parsing history token: " + token, err);
- s = null;
- }
-
- if (s != null) {
- Gerrit.display(s);
- } else {
- Gerrit.display(new NotFoundScreen());
- }
- }
-
- private Screen select(final String token) {
- String p;
-
- if (token == null) {
- return null;
- }
-
- if (SETTINGS.equals(token) || token.startsWith("settings,")) {
- if (SETTINGS_NEW_AGREEMENT.equals(token)) {
- return new NewAgreementScreen();
- }
- p = SETTINGS_NEW_AGREEMENT + ",";
- if (token.startsWith(p)) {
- return new NewAgreementScreen(skip(p, token));
- }
- return new AccountSettings(token);
- }
-
- if (MINE.equals(token)) {
- if (Gerrit.isSignedIn()) {
- return new AccountDashboardScreen(Gerrit.getUserAccount().getId());
- } else {
- final Screen r = new AccountDashboardScreen(null);
- r.setRequiresSignIn(true);
- return r;
- }
- }
- if (token.startsWith("mine,")) {
- if (MINE_STARRED.equals(token)) {
- return new MineStarredScreen();
- }
- if (MINE_DRAFTS.equals(token)) {
- return new MineDraftsScreen();
- }
- }
-
- if (token.startsWith("all,")) {
- p = "all,abandoned,";
- if (token.startsWith(p)) {
- return new AllAbandonedChangesScreen(skip(p, token));
- }
-
- p = "all,merged,";
- if (token.startsWith(p)) {
- return new AllMergedChangesScreen(skip(p, token));
- }
-
- p = "all,open,";
- if (token.startsWith(p)) {
- return new AllOpenChangesScreen(skip(p, token));
- }
- }
-
- if (token.startsWith("project,")) {
- p = "project,open,";
- if (token.startsWith(p)) {
- final String s = skip(p, token);
- final int c = s.indexOf(',');
- return new ByProjectOpenChangesScreen(Project.NameKey.parse(s
- .substring(0, c)), s.substring(c + 1));
- }
-
- p = "project,merged,";
- if (token.startsWith(p)) {
- final String s = skip(p, token);
- final int c = s.indexOf(',');
- return new ByProjectMergedChangesScreen(Project.NameKey.parse(s
- .substring(0, c)), s.substring(c + 1));
- }
-
- p = "project,abandoned,";
- if (token.startsWith(p)) {
- final String s = skip(p, token);
- final int c = s.indexOf(',');
- return new ByProjectAbandonedChangesScreen(Project.NameKey.parse(s
- .substring(0, c)), s.substring(c + 1));
- }
- }
-
- if (token.startsWith("patch,")) {
- p = "patch,sidebyside,";
- if (token.startsWith(p))
- return new PatchScreen.SideBySide(Patch.Key.parse(skip(p, token)),
- 0 /* patchIndex */, null /* patchTable */);
-
- p = "patch,unified,";
- if (token.startsWith(p))
- return new PatchScreen.Unified(Patch.Key.parse(skip(p, token)),
- 0 /* patchIndex */, null /* patchTable */);
- }
-
- p = "change,publish,";
- if (token.startsWith(p))
- return new PublishCommentScreen(PatchSet.Id.parse(skip(p, token)));
-
- p = "change,";
- if (token.startsWith(p))
- return new ChangeScreen(Change.Id.parse(skip(p, token)));
-
- p = "dashboard,";
- if (token.startsWith(p))
- return new AccountDashboardScreen(Account.Id.parse(skip(p, token)));
-
- p = "q,";
- if (token.startsWith(p)) {
- final String s = skip(p, token);
- final int c = s.indexOf(',');
- return new ChangeQueryResultsScreen(s.substring(0, c), s.substring(c + 1));
- }
-
- if (token.startsWith("admin,")) {
- p = "admin,group,";
- if (token.startsWith(p))
- return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
-
- p = "admin,project,";
- if (token.startsWith(p)) {
- p = skip(p, token);
- final int c = p.indexOf(',');
- final String idstr = p.substring(0, c);
- return new ProjectAdminScreen(Project.NameKey.parse(idstr), token);
- }
-
- if (ADMIN_GROUPS.equals(token)) {
- return new GroupListScreen();
- }
-
- if (ADMIN_PROJECTS.equals(token)) {
- return new ProjectListScreen();
- }
- }
-
- p = REGISTER + ",";
- if (token.startsWith(p)) {
- return new RegisterScreen(skip(p, token));
- } else if (REGISTER.equals(token)) {
- return new RegisterScreen(MINE);
- }
-
- p = "VE,";
- if (token.startsWith(p)) {
- return new ValidateEmailScreen(skip(p, token));
- }
-
- p = "SignInFailure,";
- if (token.startsWith(p)) {
- final String[] args = skip(p, token).split(",");
- final SignInDialog.Mode mode = SignInDialog.Mode.valueOf(args[0]);
- final String msg = KeyUtil.decode(args[1]);
- switch (Gerrit.getConfig().getAuthType()) {
- case OPENID:
- new OpenIdSignInDialog(mode, msg).center();
- break;
- case LDAP:
- new UserPassSignInDialog(msg).center();
- break;
- default:
- return null;
- }
- switch (mode) {
- case SIGN_IN:
- return select(ALL_OPEN);
- case LINK_IDENTIY:
- return new AccountSettings(SETTINGS_WEBIDENT);
- }
- }
-
- return null;
- }
-
- private static String skip(final String prefix, final String in) {
- return in.substring(prefix.length());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/SearchPanel.java b/src/main/java/com/google/gerrit/client/SearchPanel.java
deleted file mode 100644
index 5b40972022..0000000000
--- a/src/main/java/com/google/gerrit/client/SearchPanel.java
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gerrit.client.changes.ChangeScreen;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-class SearchPanel extends Composite {
- private final NpTextBox searchBox;
- private HandlerRegistration regFocus;
-
- SearchPanel() {
- final FlowPanel body = new FlowPanel();
- initWidget(body);
- setStyleName("gerrit-SearchPanel");
-
- searchBox = new NpTextBox();
- searchBox.setVisibleLength(46);
- searchBox.setText(Gerrit.C.searchHint());
- searchBox.addStyleName("gerrit-InputFieldTypeHint");
- searchBox.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- if (Gerrit.C.searchHint().equals(searchBox.getText())) {
- searchBox.setText("");
- searchBox.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- searchBox.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- if ("".equals(searchBox.getText())) {
- searchBox.setText(Gerrit.C.searchHint());
- searchBox.addStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- searchBox.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- switch (event.getCharCode()) {
- case KeyCodes.KEY_ENTER:
- doSearch();
- break;
- case KeyCodes.KEY_ESCAPE:
- searchBox.setText("");
- searchBox.setFocus(false);
- break;
- }
- }
- });
-
- final Button searchButton = new Button(Gerrit.C.searchButton());
- searchButton.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSearch();
- }
- });
-
- body.add(searchBox);
- body.add(searchButton);
- }
-
- void setText(final String query) {
- if (query == null || query.equals("")) {
- searchBox.setText(Gerrit.C.searchHint());
- searchBox.addStyleName("gerrit-InputFieldTypeHint");
- } else {
- searchBox.setText(query);
- searchBox.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- if (regFocus == null) {
- regFocus =
- GlobalKey.addApplication(this, new KeyCommand(0, '/', Gerrit.C
- .keySearch()) {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- event.preventDefault();
- searchBox.setFocus(true);
- searchBox.selectAll();
- }
- });
- }
- }
-
- @Override
- protected void onUnload() {
- if (regFocus != null) {
- regFocus.removeHandler();
- regFocus = null;
- }
- }
-
- private void doSearch() {
- final String query = searchBox.getText().trim();
- if (query.length() == 0 || Gerrit.C.searchHint().equals(query)) {
- return;
- }
-
- searchBox.setFocus(false);
-
- if (query.matches("^[1-9][0-9]*$")) {
- final Change.Id ck = Change.Id.parse(query);
- Gerrit.display(Link.toChange(ck), new ChangeScreen(ck));
- } else {
- Gerrit.display(Link.toChangeQuery(query), true);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/SignInDialog.java b/src/main/java/com/google/gerrit/client/SignInDialog.java
deleted file mode 100644
index 1226533b46..0000000000
--- a/src/main/java/com/google/gerrit/client/SignInDialog.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client;
-
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-/** Prompts the user to sign in to their account. */
-public abstract class SignInDialog extends AutoCenterDialogBox {
- public static enum Mode {
- SIGN_IN, LINK_IDENTIY, REGISTER;
- }
-
- protected final SignInDialog.Mode mode;
-
- /**
- * Create a new dialog to handle user sign in.
- *
- * @param signInMode type of mode the login will perform.
- */
- protected SignInDialog(final Mode signInMode) {
- super(/* auto hide */true, /* modal */true);
- mode = signInMode;
-
- switch (signInMode) {
- case LINK_IDENTIY:
- setText(Gerrit.C.linkIdentityDialogTitle());
- break;
- case REGISTER:
- setText(Gerrit.C.registerDialogTitle());
- break;
- default:
- setText(Gerrit.C.signInDialogTitle());
- break;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AccountProjectWatchInfo.java b/src/main/java/com/google/gerrit/client/account/AccountProjectWatchInfo.java
deleted file mode 100644
index 1657f75d8a..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AccountProjectWatchInfo.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.Project;
-
-public final class AccountProjectWatchInfo {
- protected AccountProjectWatch watch;
- protected Project project;
-
- protected AccountProjectWatchInfo() {
- }
-
- public AccountProjectWatchInfo(final AccountProjectWatch w, final Project p) {
- watch = w;
- project = p;
- }
-
- public AccountProjectWatch getWatch() {
- return watch;
- }
-
- public Project getProject() {
- return project;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AccountSecurity.java b/src/main/java/com/google/gerrit/client/account/AccountSecurity.java
deleted file mode 100644
index 83bcb840a6..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AccountSecurity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.List;
-import java.util.Set;
-
-public interface AccountSecurity extends RemoteJsonService {
- @SignInRequired
- void mySshKeys(AsyncCallback<List<AccountSshKey>> callback);
-
- @SignInRequired
- void addSshKey(String keyText, AsyncCallback<AccountSshKey> callback);
-
- @SignInRequired
- void deleteSshKeys(Set<AccountSshKey.Id> ids,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void changeSshUserName(String newName, AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void myExternalIds(AsyncCallback<List<AccountExternalId>> callback);
-
- @SignInRequired
- void myGroups(AsyncCallback<List<AccountGroup>> callback);
-
- @SignInRequired
- void deleteExternalIds(Set<AccountExternalId.Key> keys,
- AsyncCallback<Set<AccountExternalId.Key>> callback);
-
- @SignInRequired
- void updateContact(String fullName, String emailAddr,
- ContactInformation info, AsyncCallback<Account> callback);
-
- @SignInRequired
- void enterAgreement(ContributorAgreement.Id id,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void registerEmail(String address, AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void validateEmail(String token, AsyncCallback<VoidResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AccountService.java b/src/main/java/com/google/gerrit/client/account/AccountService.java
deleted file mode 100644
index a707b41030..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AccountService.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.List;
-import java.util.Set;
-
-public interface AccountService extends RemoteJsonService {
- @SignInRequired
- void myAccount(AsyncCallback<Account> callback);
-
- @SignInRequired
- void changePreferences(AccountGeneralPreferences pref,
- AsyncCallback<VoidResult> gerritCallback);
-
- @SignInRequired
- void myProjectWatch(AsyncCallback<List<AccountProjectWatchInfo>> callback);
-
- @SignInRequired
- void addProjectWatch(String projectName,
- AsyncCallback<AccountProjectWatchInfo> callback);
-
- @SignInRequired
- void updateProjectWatch(AccountProjectWatch watch,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void deleteProjectWatches(Set<AccountProjectWatch.Key> keys,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void myAgreements(AsyncCallback<AgreementInfo> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AccountSettings.java b/src/main/java/com/google/gerrit/client/account/AccountSettings.java
deleted file mode 100644
index 718e0b604a..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AccountSettings.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import static com.google.gerrit.client.FormatUtil.mediumFormat;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.LazyPanel;
-import com.google.gwt.user.client.ui.TabPanel;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class AccountSettings extends AccountScreen {
- private final String initialTabToken;
- private int labelIdx, fieldIdx;
- private Grid info;
-
- private List<String> tabTokens;
- private TabPanel tabs;
-
- public AccountSettings(final String tabToken) {
- initialTabToken = tabToken;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- final int idx = tabTokens.indexOf(initialTabToken);
- tabs.selectTab(0 <= idx ? idx : 0);
- display(Gerrit.getUserAccount());
- display();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.accountSettingsHeading());
-
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
-
- info = new Grid(4, 2);
- info.setStyleName("gerrit-InfoBlock");
- info.addStyleName("gerrit-AccountInfoBlock");
- add(info);
-
- infoRow(0, Util.C.fullName());
- infoRow(1, Util.C.preferredEmail());
- infoRow(2, Util.C.registeredOn());
- infoRow(3, Util.C.accountId());
-
- final CellFormatter fmt = info.getCellFormatter();
- fmt.addStyleName(0, 0, "topmost");
- fmt.addStyleName(0, 1, "topmost");
- fmt.addStyleName(3, 0, "bottomheader");
-
- tabTokens = new ArrayList<String>();
- tabs = new TabPanel();
- tabs.setWidth("98%");
- add(tabs);
-
- tabs.add(new LazyPanel() {
- @Override
- protected PreferencePanel createWidget() {
- return new PreferencePanel();
- }
- }, Util.C.tabPreferences());
- tabTokens.add(Link.SETTINGS);
-
- tabs.add(new LazyPanel() {
- @Override
- protected ProjectWatchPanel createWidget() {
- return new ProjectWatchPanel();
- }
- }, Util.C.watchedProjects());
- tabTokens.add(Link.SETTINGS_PROJECTS);
-
- tabs.add(new LazyPanel() {
- @Override
- protected ContactPanelFull createWidget() {
- final ContactPanelFull p = new ContactPanelFull();
- p.accountSettings = AccountSettings.this;
- return p;
- }
- }, Util.C.tabContactInformation());
- tabTokens.add(Link.SETTINGS_CONTACT);
-
- tabs.add(new LazyPanel() {
- @Override
- protected SshPanel createWidget() {
- return new SshPanel();
- }
- }, Util.C.tabSshKeys());
- tabTokens.add(Link.SETTINGS_SSHKEYS);
-
- tabs.add(new LazyPanel() {
- @Override
- protected ExternalIdPanel createWidget() {
- return new ExternalIdPanel();
- }
- }, Util.C.tabWebIdentities());
- tabTokens.add(Link.SETTINGS_WEBIDENT);
-
- tabs.add(new LazyPanel() {
- @Override
- protected MyGroupsPanel createWidget() {
- return new MyGroupsPanel();
- }
- }, Util.C.tabMyGroups());
- tabTokens.add(Link.SETTINGS_MYGROUPS);
-
- if (Gerrit.getConfig().isUseContributorAgreements()) {
- tabs.add(new LazyPanel() {
- @Override
- protected AgreementPanel createWidget() {
- return new AgreementPanel();
- }
- }, Util.C.tabAgreements());
- tabTokens.add(Link.SETTINGS_AGREEMENTS);
- }
-
- tabs.addSelectionHandler(new SelectionHandler<Integer>() {
- @Override
- public void onSelection(final SelectionEvent<Integer> event) {
- Gerrit.display(tabTokens.get(event.getSelectedItem()), false);
- }
- });
- }
-
- private void infoRow(final int row, final String name) {
- info.setText(row, labelIdx, name);
- info.getCellFormatter().addStyleName(row, 0, "header");
- }
-
- void display(final Account account) {
- info.setText(0, fieldIdx, account.getFullName());
- info.setText(1, fieldIdx, account.getPreferredEmail());
- info.setText(2, fieldIdx, mediumFormat(account.getRegisteredOn()));
- info.setText(3, fieldIdx, account.getId().toString());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AgreementInfo.java b/src/main/java/com/google/gerrit/client/account/AgreementInfo.java
deleted file mode 100644
index fc1f53e7a3..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AgreementInfo.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-
-import java.util.List;
-import java.util.Map;
-
-public class AgreementInfo {
- protected List<AccountAgreement> userAccepted;
- protected List<AccountGroupAgreement> groupAccepted;
- protected Map<ContributorAgreement.Id, ContributorAgreement> agreements;
-
- public AgreementInfo() {
- }
-
- public void setUserAccepted(List<AccountAgreement> a) {
- userAccepted = a;
- }
-
- public void setGroupAccepted(List<AccountGroupAgreement> a) {
- groupAccepted = a;
- }
-
- public void setAgreements(Map<ContributorAgreement.Id, ContributorAgreement> a) {
- agreements = a;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/AgreementPanel.java b/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
deleted file mode 100644
index 27ac0bb47e..0000000000
--- a/src/main/java/com/google/gerrit/client/account/AgreementPanel.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.AbstractAgreement;
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Hyperlink;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-class AgreementPanel extends Composite {
- private AgreementTable agreements;
-
- AgreementPanel() {
- final FlowPanel body = new FlowPanel();
-
- agreements = new AgreementTable();
- body.add(agreements);
- body.add(new Hyperlink(Util.C.newAgreement(), Link.SETTINGS_NEW_AGREEMENT));
-
- initWidget(body);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
- public void onSuccess(final AgreementInfo result) {
- agreements.display(result);
- }
- });
- }
-
- private class AgreementTable extends FancyFlexTable<AbstractAgreement> {
- AgreementTable() {
- table.setWidth("");
- table.setText(0, 1, Util.C.agreementStatus());
- table.setText(0, 2, Util.C.agreementName());
- table.setText(0, 3, Util.C.agreementDescription());
- table.setText(0, 4, Util.C.agreementAccepted());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int c = 1; c <= 4; c++) {
- fmt.addStyleName(0, c, S_DATA_HEADER);
- }
- }
-
- void display(final AgreementInfo result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountAgreement k : result.userAccepted) {
- addOne(result, k);
- }
- for (final AccountGroupAgreement k : result.groupAccepted) {
- addOne(result, k);
- }
- }
-
- void addOne(final AgreementInfo info, final AbstractAgreement k) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- final ContributorAgreement cla = info.agreements.get(k.getAgreementId());
- final String statusName;
- if (cla == null || !cla.isActive()) {
- statusName = Util.C.agreementStatus_EXPIRED();
- } else {
- switch (k.getStatus()) {
- case NEW:
- statusName = Util.C.agreementStatus_NEW();
- break;
- case REJECTED:
- statusName = Util.C.agreementStatus_REJECTED();
- break;
- case VERIFIED:
- statusName = Util.C.agreementStatus_VERIFIED();
- break;
- default:
- statusName = k.getStatus().name();
- }
- }
- table.setText(row, 1, statusName);
-
- if (cla == null) {
- table.setText(row, 2, "");
- table.setText(row, 3, "");
- } else {
- final String url = cla.getAgreementUrl();
- if (url != null && url.length() > 0) {
- final Anchor a = new Anchor(cla.getShortName(), url);
- a.setTarget("_blank");
- table.setWidget(row, 2, a);
- } else {
- table.setText(row, 2, cla.getShortName());
- }
- table.setText(row, 3, cla.getShortDescription());
- }
-
- final SafeHtmlBuilder b = new SafeHtmlBuilder();
- b.append(FormatUtil.mediumFormat(k.getAcceptedOn()));
- b.br();
- b.append(FormatUtil.mediumFormat(k.getReviewedOn()));
- SafeHtml.set(table, row, 4, b);
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- for (int c = 1; c <= 4; c++) {
- fmt.addStyleName(row, c, S_DATA_CELL);
- }
- fmt.addStyleName(row, 4, "C_LAST_UPDATE");
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java b/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
deleted file mode 100644
index 13a66415a6..0000000000
--- a/src/main/java/com/google/gerrit/client/account/ContactPanelFull.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-import java.sql.Timestamp;
-import java.util.Date;
-
-class ContactPanelFull extends ContactPanelShort {
- private Label hasContact;
- private NpTextArea addressTxt;
- private NpTextBox countryTxt;
- private NpTextBox phoneTxt;
- private NpTextBox faxTxt;
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- addressTxt = new NpTextArea();
- addressTxt.setVisibleLines(4);
- addressTxt.setCharacterWidth(60);
-
- countryTxt = new NpTextBox();
- countryTxt.setVisibleLength(40);
- countryTxt.setMaxLength(40);
-
- phoneTxt = new NpTextBox();
- phoneTxt.setVisibleLength(30);
- phoneTxt.setMaxLength(30);
-
- faxTxt = new NpTextBox();
- faxTxt.setVisibleLength(30);
- faxTxt.setMaxLength(30);
-
- final Grid infoSecure = new Grid(4, 2);
- infoSecure.setStyleName("gerrit-InfoBlock");
- infoSecure.addStyleName("gerrit-AccountInfoBlock");
-
- final HTML privhtml = new HTML(Util.C.contactPrivacyDetailsHtml());
- privhtml.setStyleName("gerrit-AccountContactPrivacyDetails");
-
- hasContact = new Label();
- hasContact.setStyleName("gerrit-AccountContactOnFile");
- hasContact.setVisible(false);
-
- if (Gerrit.getConfig().isUseContactInfo()) {
- body.add(privhtml);
- body.add(hasContact);
- body.add(infoSecure);
- }
-
- row(infoSecure, 0, Util.C.contactFieldAddress(), addressTxt);
- row(infoSecure, 1, Util.C.contactFieldCountry(), countryTxt);
- row(infoSecure, 2, Util.C.contactFieldPhone(), phoneTxt);
- row(infoSecure, 3, Util.C.contactFieldFax(), faxTxt);
-
- infoSecure.getCellFormatter().addStyleName(0, 0, "topmost");
- infoSecure.getCellFormatter().addStyleName(0, 1, "topmost");
- infoSecure.getCellFormatter().addStyleName(3, 0, "bottomheader");
-
- final TextSaveButtonListener sbl = new TextSaveButtonListener(save);
- addressTxt.addKeyPressHandler(sbl);
- countryTxt.addKeyPressHandler(sbl);
- phoneTxt.addKeyPressHandler(sbl);
- faxTxt.addKeyPressHandler(sbl);
- }
-
- @Override
- protected void display(final Account userAccount) {
- super.display(userAccount);
- displayHasContact(userAccount);
- addressTxt.setText("");
- countryTxt.setText("");
- phoneTxt.setText("");
- faxTxt.setText("");
- }
-
- private void displayHasContact(final Account userAccount) {
- if (userAccount.isContactFiled()) {
- final Timestamp dt = userAccount.getContactFiledOn();
- hasContact.setText(Util.M.contactOnFile(new Date(dt.getTime())));
- hasContact.setVisible(true);
- } else {
- hasContact.setVisible(false);
- }
- }
-
- @Override
- void onSaveSuccess(final Account userAccount) {
- super.onSaveSuccess(userAccount);
- displayHasContact(userAccount);
- }
-
- @Override
- ContactInformation toContactInformation() {
- final ContactInformation info;
- if (Gerrit.getConfig().isUseContactInfo()) {
- info = new ContactInformation();
- info.setAddress(addressTxt.getText());
- info.setCountry(countryTxt.getText());
- info.setPhoneNumber(phoneTxt.getText());
- info.setFaxNumber(faxTxt.getText());
- } else {
- info = null;
- }
- return info;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
deleted file mode 100644
index 7995006fd3..0000000000
--- a/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class ContactPanelShort extends Composite {
- AccountSettings accountSettings;
-
- protected final FlowPanel body;
- protected int labelIdx, fieldIdx;
- protected Button save;
-
- private String currentEmail;
- protected boolean haveAccount;
- private boolean haveEmails;
-
- NpTextBox nameTxt;
- private ListBox emailPick;
- private Button registerNewEmail;
-
- ContactPanelShort() {
- body = new FlowPanel();
- initWidget(body);
- }
-
- protected void onInitUI() {
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
-
- nameTxt = new NpTextBox();
- nameTxt.setVisibleLength(60);
- nameTxt.setReadOnly(!canEditFullName());
-
- emailPick = new ListBox();
-
- final Grid infoPlainText = new Grid(2, 2);
- infoPlainText.setStyleName("gerrit-InfoBlock");
- infoPlainText.addStyleName("gerrit-AccountInfoBlock");
-
- body.add(infoPlainText);
-
- registerNewEmail = new Button(Util.C.buttonOpenRegisterNewEmail());
- registerNewEmail.setEnabled(false);
- registerNewEmail.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doRegisterNewEmail();
- }
- });
- final FlowPanel emailLine = new FlowPanel();
- emailLine.add(emailPick);
- if (canRegisterNewEmail()) {
- emailLine.add(registerNewEmail);
- }
-
- row(infoPlainText, 0, Util.C.contactFieldFullName(), nameTxt);
- row(infoPlainText, 1, Util.C.contactFieldEmail(), emailLine);
-
- infoPlainText.getCellFormatter().addStyleName(0, 0, "topmost");
- infoPlainText.getCellFormatter().addStyleName(0, 1, "topmost");
- infoPlainText.getCellFormatter().addStyleName(1, 0, "bottomheader");
-
- save = new Button(Util.C.buttonSaveChanges());
- save.setEnabled(false);
- save.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doSave();
- }
- });
-
- final TextSaveButtonListener sbl = new TextSaveButtonListener(save);
- nameTxt.addKeyPressHandler(sbl);
- emailPick.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(final ChangeEvent event) {
- final int idx = emailPick.getSelectedIndex();
- final String v = 0 <= idx ? emailPick.getValue(idx) : null;
- if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
- for (int i = 0; i < emailPick.getItemCount(); i++) {
- if (currentEmail.equals(emailPick.getValue(i))) {
- emailPick.setSelectedIndex(i);
- break;
- }
- }
- doRegisterNewEmail();
- } else {
- save.setEnabled(true);
- }
- }
- });
- }
-
- private boolean canEditFullName() {
- return Gerrit.getConfig().canEdit(Account.FieldName.FULL_NAME);
- }
-
- private boolean canRegisterNewEmail() {
- return Gerrit.getConfig().canEdit(Account.FieldName.REGISTER_NEW_EMAIL);
- }
-
- void hideSaveButton() {
- save.setVisible(false);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- onInitUI();
- body.add(save);
- display(Gerrit.getUserAccount());
-
- emailPick.clear();
- emailPick.setEnabled(false);
- registerNewEmail.setEnabled(false);
-
- haveAccount = false;
- haveEmails = false;
-
- Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
- public void onSuccess(final Account result) {
- if (!isAttached()) {
- return;
- }
- display(result);
- haveAccount = true;
- postLoad();
- }
- });
- Util.ACCOUNT_SEC
- .myExternalIds(new GerritCallback<List<AccountExternalId>>() {
- public void onSuccess(final List<AccountExternalId> result) {
- if (!isAttached()) {
- return;
- }
- final Set<String> emails = new HashSet<String>();
- for (final AccountExternalId i : result) {
- if (i.getEmailAddress() != null
- && i.getEmailAddress().length() > 0) {
- emails.add(i.getEmailAddress());
- }
- }
- final List<String> addrs = new ArrayList<String>(emails);
- Collections.sort(addrs);
- for (String s : addrs) {
- emailPick.addItem(s);
- }
- haveEmails = true;
- postLoad();
- }
- });
- }
-
- private void postLoad() {
- if (haveAccount && haveEmails) {
- if (currentEmail != null) {
- boolean found = false;
- for (int i = 0; i < emailPick.getItemCount(); i++) {
- if (currentEmail.equals(emailPick.getValue(i))) {
- emailPick.setSelectedIndex(i);
- found = true;
- break;
- }
- }
- if (!found) {
- emailPick.addItem(currentEmail);
- emailPick.setSelectedIndex(emailPick.getItemCount() - 1);
- }
- }
- if (emailPick.getItemCount() > 0) {
- emailPick.setVisible(true);
- emailPick.setEnabled(true);
- if (canRegisterNewEmail()) {
- final String t = Util.C.buttonOpenRegisterNewEmail();
- emailPick.addItem("... " + t + " ", t);
- }
- } else {
- emailPick.setVisible(false);
- }
- registerNewEmail.setEnabled(true);
- }
- }
-
- protected void row(final Grid info, final int row, final String name,
- final Widget field) {
- info.setText(row, labelIdx, name);
- info.setWidget(row, fieldIdx, field);
- info.getCellFormatter().addStyleName(row, 0, "header");
- }
-
- protected void display(final Account userAccount) {
- currentEmail = userAccount.getPreferredEmail();
- nameTxt.setText(userAccount.getFullName());
- save.setEnabled(false);
- }
-
- private void doRegisterNewEmail() {
- if (!canRegisterNewEmail()) {
- return;
- }
-
- final AutoCenterDialogBox box = new AutoCenterDialogBox(true, true);
- final VerticalPanel body = new VerticalPanel();
-
- final NpTextBox inEmail = new NpTextBox();
- inEmail.setVisibleLength(60);
-
- final Button register = new Button(Util.C.buttonSendRegisterNewEmail());
- final FormPanel form = new FormPanel();
- form.addSubmitHandler(new FormPanel.SubmitHandler() {
- @Override
- public void onSubmit(final SubmitEvent event) {
- event.cancel();
- final String addr = inEmail.getText().trim();
- if (!addr.contains("@")) {
- return;
- }
-
- inEmail.setEnabled(false);
- register.setEnabled(false);
- Util.ACCOUNT_SEC.registerEmail(addr, new GerritCallback<VoidResult>() {
- public void onSuccess(VoidResult result) {
- box.hide();
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- inEmail.setEnabled(true);
- register.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
- });
- form.setWidget(body);
-
- register.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- form.submit();
- }
- });
- body.add(new HTML(Util.C.descRegisterNewEmail()));
- body.add(inEmail);
- body.add(register);
-
- box.setText(Util.C.titleRegisterNewEmail());
- box.setWidget(form);
- box.center();
- inEmail.setFocus(true);
- }
-
- void doSave() {
- String newName = canEditFullName() ? nameTxt.getText() : null;
- if ("".equals(newName)) {
- newName = null;
- }
-
- final String newEmail;
- if (emailPick.isEnabled() && emailPick.getSelectedIndex() >= 0) {
- final String v = emailPick.getValue(emailPick.getSelectedIndex());
- if (Util.C.buttonOpenRegisterNewEmail().equals(v)) {
- newEmail = currentEmail;
- } else {
- newEmail = v;
- }
- } else {
- newEmail = currentEmail;
- }
-
- final ContactInformation info = toContactInformation();
- save.setEnabled(false);
- registerNewEmail.setEnabled(false);
-
- Util.ACCOUNT_SEC.updateContact(newName, newEmail, info,
- new GerritCallback<Account>() {
- public void onSuccess(final Account result) {
- registerNewEmail.setEnabled(true);
- onSaveSuccess(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- save.setEnabled(true);
- registerNewEmail.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- void onSaveSuccess(final Account result) {
- final Account me = Gerrit.getUserAccount();
- me.setFullName(result.getFullName());
- me.setPreferredEmail(result.getPreferredEmail());
- me.setSshUserName(result.getSshUserName());
- Gerrit.refreshMenuBar();
- if (accountSettings != null) {
- accountSettings.display(me);
- }
- }
-
- ContactInformation toContactInformation() {
- return null;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java b/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java
deleted file mode 100644
index 56ad107b2b..0000000000
--- a/src/main/java/com/google/gerrit/client/account/ExternalIdPanel.java
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.SignInDialog;
-import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
-import com.google.gerrit.client.auth.openid.OpenIdUtil;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class ExternalIdPanel extends Composite {
- private IdTable identites;
- private Button deleteIdentity;
-
- ExternalIdPanel() {
- final FlowPanel body = new FlowPanel();
-
- identites = new IdTable();
- body.add(identites);
-
- deleteIdentity = new Button(Util.C.buttonDeleteIdentity());
- deleteIdentity.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- identites.deleteChecked();
- }
- });
- body.add(deleteIdentity);
-
- switch (Gerrit.getConfig().getAuthType()) {
- case OPENID: {
- final Button linkIdentity = new Button(Util.C.buttonLinkIdentity());
- linkIdentity.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- new OpenIdSignInDialog(SignInDialog.Mode.LINK_IDENTIY, null).center();
- }
- });
- body.add(linkIdentity);
- break;
- }
- }
-
- initWidget(body);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refresh();
- }
-
- private void refresh() {
- Util.ACCOUNT_SEC
- .myExternalIds(new GerritCallback<List<AccountExternalId>>() {
- public void onSuccess(final List<AccountExternalId> result) {
- identites.display(result);
- }
- });
- }
-
- private class IdTable extends FancyFlexTable<AccountExternalId> {
- IdTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.webIdLastUsed());
- table.setText(0, 3, Util.C.webIdStatus());
- table.setText(0, 4, Util.C.webIdEmail());
- table.setText(0, 5, Util.C.webIdIdentity());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- fmt.addStyleName(0, 4, S_DATA_HEADER);
- fmt.addStyleName(0, 5, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<AccountExternalId.Key> keys =
- new HashSet<AccountExternalId.Key>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountExternalId k = getRowItem(row);
- if (k == null) {
- continue;
- }
- final CheckBox cb = (CheckBox) table.getWidget(row, 1);
- if (cb == null) {
- continue;
- }
- if (cb.getValue()) {
- keys.add(k.getKey());
- }
- }
- if (!keys.isEmpty()) {
- deleteIdentity.setEnabled(false);
- Util.ACCOUNT_SEC.deleteExternalIds(keys,
- new GerritCallback<Set<AccountExternalId.Key>>() {
- public void onSuccess(final Set<AccountExternalId.Key> removed) {
- deleteIdentity.setEnabled(true);
- for (int row = 1; row < table.getRowCount();) {
- final AccountExternalId k = getRowItem(row);
- if (k != null && removed.contains(k.getKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- deleteIdentity.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
- }
-
- void display(final List<AccountExternalId> result) {
- Collections.sort(result, new Comparator<AccountExternalId>() {
- @Override
- public int compare(AccountExternalId a, AccountExternalId b) {
- return emailOf(a).compareTo(emailOf(b));
- }
-
- private String emailOf(final AccountExternalId a) {
- return a.getEmailAddress() != null ? a.getEmailAddress() : "";
- }
- });
-
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountExternalId k : result) {
- addOneId(k);
- }
-
- final AccountExternalId mostRecent = AccountExternalId.mostRecent(result);
- if (mostRecent != null) {
- for (int row = 1; row < table.getRowCount(); row++) {
- if (getRowItem(row) == mostRecent) {
- // Remove the box from the most recent row, this prevents
- // the user from trying to delete the identity they last used
- // to login, possibly locking themselves out of the account.
- //
- table.setHTML(row, 1, "&nbsp;");
- break;
- }
- }
- }
- }
-
- void addOneId(final AccountExternalId k) {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- table.setWidget(row, 1, new CheckBox());
- if (k.getLastUsedOn() != null) {
- table.setText(row, 2, FormatUtil.mediumFormat(k.getLastUsedOn()));
- } else {
- table.setText(row, 2, "");
- }
- if (k.isTrusted()) {
- table.setText(row, 3, "");
- } else {
- table.setText(row, 3, Util.C.untrustedProvider());
- fmt.addStyleName(row, 3, "gerrit-Identity-UntrustedExternalId");
- }
- if (k.getEmailAddress() != null && k.getEmailAddress().length() > 0) {
- table.setText(row, 4, k.getEmailAddress());
- } else {
- table.setText(row, 4, "");
- }
- table.setText(row, 5, describe(k));
-
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_DATA_CELL);
- fmt.addStyleName(row, 3, S_DATA_CELL);
- fmt.addStyleName(row, 3, "C_LAST_UPDATE");
- fmt.addStyleName(row, 4, S_DATA_CELL);
- fmt.addStyleName(row, 5, S_DATA_CELL);
-
- setRowItem(row, k);
- }
-
- private String describe(final AccountExternalId k) {
- if (k.isScheme(AccountExternalId.SCHEME_GERRIT)) {
- // A local user identity should just be itself.
- //
- return k.getSchemeRest(AccountExternalId.SCHEME_GERRIT);
-
- } else if (k.isScheme(AccountExternalId.SCHEME_MAILTO)) {
- // Describe a mailto address as just its email address, which
- // is already shown in the email address field.
- //
- return "";
-
- } else if (k.isScheme(OpenIdUtil.URL_GOOGLE)) {
- return OpenIdUtil.C.nameGoogle();
-
- } else if (k.isScheme(OpenIdUtil.URL_YAHOO)) {
- return OpenIdUtil.C.nameYahoo();
-
- } else if (k.isScheme(AccountExternalId.LEGACY_GAE)) {
- return OpenIdUtil.C.nameGoogle() + " (Imported from Google AppEngine)";
-
- } else {
- return k.getExternalId();
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java b/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java
deleted file mode 100644
index 6e7e32bb8c..0000000000
--- a/src/main/java/com/google/gerrit/client/account/MyGroupsPanel.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.admin.GroupTable;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-
-import java.util.List;
-
-class MyGroupsPanel extends Composite {
- private GroupTable groups;
-
- MyGroupsPanel() {
- final FlowPanel body = new FlowPanel();
-
- groups = new GroupTable(false /* do not hyperlink to admin */);
- body.add(groups);
-
- initWidget(body);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refresh();
- }
-
- private void refresh() {
- Util.ACCOUNT_SEC.myGroups(new GerritCallback<List<AccountGroup>>() {
- public void onSuccess(final List<AccountGroup> result) {
- groups.display(result);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java b/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
deleted file mode 100644
index 64e6f33bbc..0000000000
--- a/src/main/java/com/google/gerrit/client/account/NewAgreementScreen.java
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.http.client.Request;
-import com.google.gwt.http.client.RequestBuilder;
-import com.google.gwt.http.client.RequestCallback;
-import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class NewAgreementScreen extends AccountScreen {
- private final String nextToken;
- private Set<ContributorAgreement.Id> mySigned;
- private List<ContributorAgreement> available;
- private ContributorAgreement current;
-
- private VerticalPanel radios;
-
- private Panel agreementGroup;
- private HTML agreementHtml;
-
- private Panel contactGroup;
- private ContactPanelFull contactPanel;
-
- private Panel finalGroup;
- private NpTextBox yesIAgreeBox;
- private Button submit;
-
- public NewAgreementScreen() {
- this(null);
- }
-
- public NewAgreementScreen(final String token) {
- nextToken = token != null ? token : Link.SETTINGS_AGREEMENTS;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.ACCOUNT_SVC.myAgreements(new GerritCallback<AgreementInfo>() {
- public void onSuccess(AgreementInfo result) {
- if (isAttached()) {
- mySigned = new HashSet<ContributorAgreement.Id>();
- for (AccountAgreement a : result.userAccepted) {
- mySigned.add(a.getAgreementId());
- }
- for (AccountGroupAgreement a : result.groupAccepted) {
- mySigned.add(a.getAgreementId());
- }
- postRPC();
- }
- }
- });
- Gerrit.SYSTEM_SVC
- .contributorAgreements(new GerritCallback<List<ContributorAgreement>>() {
- public void onSuccess(final List<ContributorAgreement> result) {
- if (isAttached()) {
- available = result;
- postRPC();
- }
- }
- });
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.newAgreement());
-
- final FlowPanel formBody = new FlowPanel();
- radios = new VerticalPanel();
- formBody.add(radios);
-
- agreementGroup = new FlowPanel();
- agreementGroup
- .add(new SmallHeading(Util.C.newAgreementReviewLegalHeading()));
-
- agreementHtml = new HTML();
- agreementHtml.setStyleName("gerrit-ContributorAgreement-Legal");
- agreementGroup.add(agreementHtml);
- formBody.add(agreementGroup);
-
- contactGroup = new FlowPanel();
- contactGroup
- .add(new SmallHeading(Util.C.newAgreementReviewContactHeading()));
- formBody.add(contactGroup);
-
- finalGroup = new VerticalPanel();
- finalGroup.add(new SmallHeading(Util.C.newAgreementCompleteHeading()));
- final FlowPanel fp = new FlowPanel();
- yesIAgreeBox = new NpTextBox();
- yesIAgreeBox.setVisibleLength(Util.C.newAgreementIAGREE().length() + 8);
- yesIAgreeBox.setMaxLength(Util.C.newAgreementIAGREE().length());
- fp.add(yesIAgreeBox);
- fp.add(new InlineLabel(Util.M.enterIAGREE(Util.C.newAgreementIAGREE())));
- finalGroup.add(fp);
- submit = new Button(Util.C.buttonSubmitNewAgreement());
- submit.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doSign();
- }
- });
- finalGroup.add(submit);
- formBody.add(finalGroup);
- new TextSaveButtonListener(yesIAgreeBox, submit);
-
- final FormPanel form = new FormPanel();
- form.add(formBody);
- add(form);
- }
-
- private void postRPC() {
- if (mySigned != null && available != null) {
- renderSelf();
- display();
- }
- }
-
- private void renderSelf() {
- current = null;
- agreementGroup.setVisible(false);
- contactGroup.setVisible(false);
- finalGroup.setVisible(false);
- radios.clear();
-
- final SmallHeading hdr = new SmallHeading();
- if (available.isEmpty()) {
- hdr.setText(Util.C.newAgreementNoneAvailable());
- } else {
- hdr.setText(Util.C.newAgreementSelectTypeHeading());
- }
- radios.add(hdr);
-
- for (final ContributorAgreement cla : available) {
- final RadioButton r = new RadioButton("cla_id", cla.getShortName());
- r.addStyleName("gerrit-ContributorAgreement-Button");
- radios.add(r);
-
- if (mySigned.contains(cla.getId())) {
- r.setEnabled(false);
- final Label l = new Label(Util.C.newAgreementAlreadySubmitted());
- l.setStyleName("gerrit-ContributorAgreement-AlreadySubmitted");
- radios.add(l);
- } else {
- r.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- showCLA(cla);
- }
- });
- }
-
- if (cla.getShortDescription() != null
- && !cla.getShortDescription().equals("")) {
- final Label l = new Label(cla.getShortDescription());
- l.setStyleName("gerrit-ContributorAgreement-ShortDescription");
- radios.add(l);
- }
- }
- }
-
- private void doSign() {
- submit.setEnabled(false);
-
- if (current == null
- || !Util.C.newAgreementIAGREE()
- .equalsIgnoreCase(yesIAgreeBox.getText())) {
- yesIAgreeBox.setText("");
- yesIAgreeBox.setFocus(true);
- return;
- }
-
- if (contactGroup.isVisible()) {
- contactPanel.doSave();
- }
- Util.ACCOUNT_SEC.enterAgreement(current.getId(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- Gerrit.display(nextToken, true);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- yesIAgreeBox.setText("");
- super.onFailure(caught);
- }
- });
- }
-
- private void showCLA(final ContributorAgreement cla) {
- current = cla;
- String url = cla.getAgreementUrl();
- if (url != null && url.length() > 0) {
- agreementGroup.setVisible(true);
- agreementHtml.setText(Gerrit.C.rpcStatusLoading());
- if (!url.startsWith("http:") && !url.startsWith("https:")) {
- url = GWT.getHostPageBaseURL() + url;
- }
- final RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
- rb.setCallback(new RequestCallback() {
- public void onError(Request request, Throwable exception) {
- new ErrorDialog(exception).center();
- }
-
- public void onResponseReceived(Request request, Response response) {
- final String ct = response.getHeader("Content-Type");
- if (response.getStatusCode() == 200 && ct != null
- && (ct.equals("text/html") || ct.startsWith("text/html;"))) {
- agreementHtml.setHTML(response.getText());
- } else {
- new ErrorDialog(response.getStatusText()).center();
- }
- }
- });
- try {
- rb.send();
- } catch (RequestException e) {
- new ErrorDialog(e).show();
- }
- } else {
- agreementGroup.setVisible(false);
- }
-
- if (contactPanel == null && cla.isRequireContactInformation()) {
- contactPanel = new ContactPanelFull();
- contactGroup.add(contactPanel);
- contactPanel.hideSaveButton();
- }
- contactGroup.setVisible(cla.isRequireContactInformation());
- finalGroup.setVisible(true);
- yesIAgreeBox.setText("");
- submit.setEnabled(false);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/PreferencePanel.java b/src/main/java/com/google/gerrit/client/account/PreferencePanel.java
deleted file mode 100644
index 9102f66184..0000000000
--- a/src/main/java/com/google/gerrit/client/account/PreferencePanel.java
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.CONTEXT_CHOICES;
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.DEFAULT_CONTEXT;
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.DEFAULT_PAGESIZE;
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.PAGESIZE_CHOICES;
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-class PreferencePanel extends Composite {
- private CheckBox showSiteHeader;
- private CheckBox useFlashClipboard;
- private ListBox defaultContext;
- private ListBox maximumPageSize;
- private Button save;
-
- PreferencePanel() {
- final FlowPanel body = new FlowPanel();
-
- final ClickHandler onClickSave = new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- save.setEnabled(true);
- }
- };
- final ChangeHandler onChangeSave = new ChangeHandler() {
- @Override
- public void onChange(final ChangeEvent event) {
- save.setEnabled(true);
- }
- };
-
- showSiteHeader = new CheckBox(Util.C.showSiteHeader());
- showSiteHeader.addClickHandler(onClickSave);
-
- useFlashClipboard = new CheckBox(Util.C.useFlashClipboard());
- useFlashClipboard.addClickHandler(onClickSave);
-
- maximumPageSize = new ListBox();
- for (final short v : PAGESIZE_CHOICES) {
- maximumPageSize.addItem(Util.M.rowsPerPage(v), String.valueOf(v));
- }
- maximumPageSize.addChangeHandler(onChangeSave);
-
- defaultContext = new ListBox();
- for (final short v : CONTEXT_CHOICES) {
- final String label;
- if (v == WHOLE_FILE_CONTEXT) {
- label = Util.C.contextWholeFile();
- } else {
- label = Util.M.lines(v);
- }
- defaultContext.addItem(label, String.valueOf(v));
- }
- defaultContext.addChangeHandler(onChangeSave);
-
- final int labelIdx, fieldIdx;
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
- final Grid formGrid = new Grid(4, 2);
-
- int row = 0;
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, showSiteHeader);
- row++;
-
- formGrid.setText(row, labelIdx, "");
- formGrid.setWidget(row, fieldIdx, useFlashClipboard);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.maximumPageSizeFieldLabel());
- formGrid.setWidget(row, fieldIdx, maximumPageSize);
- row++;
-
- formGrid.setText(row, labelIdx, Util.C.defaultContextFieldLabel());
- formGrid.setWidget(row, fieldIdx, defaultContext);
- row++;
-
- body.add(formGrid);
-
- save = new Button(Util.C.buttonSaveChanges());
- save.setEnabled(false);
- save.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doSave();
- }
- });
- body.add(save);
-
- initWidget(body);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
- public void onSuccess(final Account result) {
- display(result.getGeneralPreferences());
- enable(true);
- }
- });
- }
-
- private void enable(final boolean on) {
- showSiteHeader.setEnabled(on);
- useFlashClipboard.setEnabled(on);
- maximumPageSize.setEnabled(on);
- defaultContext.setEnabled(on);
- }
-
- private void display(final AccountGeneralPreferences p) {
- showSiteHeader.setValue(p.isShowSiteHeader());
- useFlashClipboard.setValue(p.isUseFlashClipboard());
- setListBox(maximumPageSize, DEFAULT_PAGESIZE, p.getMaximumPageSize());
- setListBox(defaultContext, DEFAULT_CONTEXT, p.getDefaultContext());
- }
-
- private void setListBox(final ListBox f, final short defaultValue,
- final short currentValue) {
- final int n = f.getItemCount();
- for (int i = 0; i < n; i++) {
- if (Short.parseShort(f.getValue(i)) == currentValue) {
- f.setSelectedIndex(i);
- return;
- }
- }
- if (currentValue != defaultValue) {
- setListBox(f, defaultValue, defaultValue);
- }
- }
-
- private short getListBox(final ListBox f, final short defaultValue) {
- final int idx = f.getSelectedIndex();
- if (0 <= idx) {
- return Short.parseShort(f.getValue(idx));
- }
- return defaultValue;
- }
-
- private void doSave() {
- final AccountGeneralPreferences p = new AccountGeneralPreferences();
- p.setShowSiteHeader(showSiteHeader.getValue());
- p.setUseFlashClipboard(useFlashClipboard.getValue());
- p.setMaximumPageSize(getListBox(maximumPageSize, DEFAULT_PAGESIZE));
- p.setDefaultContext(getListBox(defaultContext, DEFAULT_CONTEXT));
-
- enable(false);
- save.setEnabled(false);
-
- Util.ACCOUNT_SVC.changePreferences(p, new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(final VoidResult result) {
- Gerrit.getUserAccount().setGeneralPreferences(p);
- Gerrit.applyUserPreferences();
- enable(true);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- enable(true);
- save.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java b/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
deleted file mode 100644
index aa45522cf8..0000000000
--- a/src/main/java/com/google/gerrit/client/account/ProjectWatchPanel.java
+++ /dev/null
@@ -1,312 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.Change.Status;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.ProjectNameSuggestOracle;
-import com.google.gerrit.client.ui.ProjectLink;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.HashSet;
-import java.util.List;
-
-class ProjectWatchPanel extends Composite {
- private WatchTable watches;
-
- private Button addNew;
- private SuggestBox nameTxt;
- private Button delSel;
-
- ProjectWatchPanel() {
- final FlowPanel body = new FlowPanel();
-
- {
- final FlowPanel fp = new FlowPanel();
- fp.setStyleName("gerrit-ProjectWatchPanel-AddPanel");
-
- final NpTextBox box = new NpTextBox();
- nameTxt = new SuggestBox(new ProjectNameSuggestOracle(), box);
- box.setVisibleLength(50);
- box.setText(Util.C.defaultProjectName());
- box.addStyleName("gerrit-InputFieldTypeHint");
- box.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- if (Util.C.defaultProjectName().equals(box.getText())) {
- box.setText("");
- box.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- box.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- if ("".equals(box.getText())) {
- box.setText(Util.C.defaultProjectName());
- box.addStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- fp.add(nameTxt);
-
- addNew = new Button(Util.C.buttonWatchProject());
- addNew.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNew();
- }
- });
- fp.add(addNew);
- body.add(fp);
- }
-
- watches = new WatchTable();
- body.add(watches);
- {
- final FlowPanel fp = new FlowPanel();
- delSel = new Button(Util.C.buttonDeleteSshKey());
- delSel.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- watches.deleteChecked();
- }
- });
- fp.add(delSel);
- body.add(fp);
- }
-
- initWidget(body);
- }
-
- void doAddNew() {
- final String projectName = nameTxt.getText();
- if (projectName == null || projectName.length() == 0
- || Util.C.defaultProjectName().equals(projectName)) {
- return;
- }
-
- addNew.setEnabled(false);
- Util.ACCOUNT_SVC.addProjectWatch(projectName,
- new GerritCallback<AccountProjectWatchInfo>() {
- public void onSuccess(final AccountProjectWatchInfo result) {
- addNew.setEnabled(true);
- nameTxt.setText("");
- watches.insertWatch(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addNew.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.ACCOUNT_SVC
- .myProjectWatch(new GerritCallback<List<AccountProjectWatchInfo>>() {
- public void onSuccess(final List<AccountProjectWatchInfo> result) {
- watches.display(result);
- }
- });
- }
-
- private class WatchTable extends FancyFlexTable<AccountProjectWatchInfo> {
- WatchTable() {
- table.setWidth("");
- table.insertRow(1);
- table.setText(0, 2, com.google.gerrit.client.changes.Util.C
- .changeTableColumnProject());
- table.setText(0, 3, Util.C.watchedProjectColumnEmailNotifications());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- fmt.setRowSpan(0, 0, 2);
- fmt.setRowSpan(0, 1, 2);
- fmt.setRowSpan(0, 2, 2);
- DOM.setElementProperty(fmt.getElement(0, 3), "align", "center");
-
- fmt.setColSpan(0, 3, 3);
- table.setText(1, 0, Util.C.watchedProjectColumnNewChanges());
- table.setText(1, 1, Util.C.watchedProjectColumnAllComments());
- table.setText(1, 2, Util.C.watchedProjectColumnSubmittedChanges());
- fmt.addStyleName(1, 0, S_DATA_HEADER);
- fmt.addStyleName(1, 1, S_DATA_HEADER);
- fmt.addStyleName(1, 2, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<AccountProjectWatch.Key> ids =
- new HashSet<AccountProjectWatch.Key>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountProjectWatchInfo k = getRowItem(row);
- if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(k.getWatch().getKey());
- }
- }
- if (!ids.isEmpty()) {
- Util.ACCOUNT_SVC.deleteProjectWatches(ids,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- for (int row = 1; row < table.getRowCount();) {
- final AccountProjectWatchInfo k = getRowItem(row);
- if (k != null && ids.contains(k.getWatch().getKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
- }
-
- void insertWatch(final AccountProjectWatchInfo k) {
- final String newName = k.getProject().getName();
- int row = 1;
- for (; row < table.getRowCount(); row++) {
- final AccountProjectWatchInfo i = getRowItem(row);
- if (i != null && i.getProject().getName().compareTo(newName) >= 0) {
- break;
- }
- }
-
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
-
- void display(final List<AccountProjectWatchInfo> result) {
- while (2 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountProjectWatchInfo k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(final int row, final AccountProjectWatchInfo k) {
- table.setWidget(row, 1, new CheckBox());
- table.setWidget(row, 2, new ProjectLink(k.getProject().getNameKey(), Status.NEW));
- {
- final CheckBox notifyNewChanges = new CheckBox();
- notifyNewChanges.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final boolean oldVal = k.getWatch().isNotifyNewChanges();
- k.getWatch().setNotifyNewChanges(notifyNewChanges.getValue());
- Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- k.getWatch().setNotifyNewChanges(oldVal);
- notifyNewChanges.setValue(oldVal);
- super.onFailure(caught);
- }
- });
- }
- });
- notifyNewChanges.setValue(k.getWatch().isNotifyNewChanges());
- table.setWidget(row, 3, notifyNewChanges);
- }
- {
- final CheckBox notifyAllComments = new CheckBox();
- notifyAllComments.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final boolean oldVal = k.getWatch().isNotifyAllComments();
- k.getWatch().setNotifyAllComments(notifyAllComments.getValue());
- Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- k.getWatch().setNotifyAllComments(oldVal);
- notifyAllComments.setValue(oldVal);
- super.onFailure(caught);
- }
- });
- }
- });
- notifyAllComments.setValue(k.getWatch().isNotifyAllComments());
- table.setWidget(row, 4, notifyAllComments);
- }
- {
- final CheckBox notifySubmittedChanges = new CheckBox();
- notifySubmittedChanges.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final boolean oldVal = k.getWatch().isNotifySubmittedChanges();
- k.getWatch().setNotifySubmittedChanges(
- notifySubmittedChanges.getValue());
- Util.ACCOUNT_SVC.updateProjectWatch(k.getWatch(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- k.getWatch().setNotifySubmittedChanges(oldVal);
- notifySubmittedChanges.setValue(oldVal);
- super.onFailure(caught);
- }
- });
- }
- });
- notifySubmittedChanges
- .setValue(k.getWatch().isNotifySubmittedChanges());
- table.setWidget(row, 5, notifySubmittedChanges);
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_DATA_CELL);
- fmt.addStyleName(row, 3, S_DATA_CELL);
- fmt.addStyleName(row, 4, S_DATA_CELL);
- fmt.addStyleName(row, 5, S_DATA_CELL);
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/RegisterScreen.java b/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
deleted file mode 100644
index 918b906316..0000000000
--- a/src/main/java/com/google/gerrit/client/account/RegisterScreen.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.InlineHyperlink;
-
-public class RegisterScreen extends AccountScreen {
- private final String nextToken;
-
- public RegisterScreen(final String next) {
- nextToken = next;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- display();
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.welcomeToGerritCodeReview());
-
- final FlowPanel formBody = new FlowPanel();
-
- final FlowPanel contactGroup = new FlowPanel();
- contactGroup.setStyleName("gerrit-RegisterScreen-Section");
- contactGroup.add(new SmallHeading(Util.C.welcomeReviewContact()));
- final HTML whereFrom = new HTML(Util.C.welcomeContactFrom());
- whereFrom.setStyleName("gerrit-RegisterScreen-Explain");
- contactGroup.add(whereFrom);
- contactGroup.add(new ContactPanelShort() {
- @Override
- protected void display(final Account userAccount) {
- super.display(userAccount);
-
- if ("".equals(nameTxt.getText())) {
- // No name? Encourage the user to provide us something.
- //
- nameTxt.setFocus(true);
- save.setEnabled(true);
- }
- }
- });
- formBody.add(contactGroup);
-
- final FlowPanel sshKeyGroup = new FlowPanel();
- sshKeyGroup.setStyleName("gerrit-RegisterScreen-Section");
- sshKeyGroup.add(new SmallHeading(Util.C.welcomeSshKeyHeading()));
- final HTML whySshKey = new HTML(Util.C.welcomeSshKeyText());
- whySshKey.setStyleName("gerrit-RegisterScreen-Explain");
- sshKeyGroup.add(whySshKey);
- sshKeyGroup.add(new SshPanel() {
- {
- setKeyTableVisible(false);
- }
- });
- formBody.add(sshKeyGroup);
-
- final FlowPanel choices = new FlowPanel();
- choices.setStyleName("gerrit-RegisterScreen-NextLinks");
- if (Gerrit.getConfig().isUseContributorAgreements()) {
- final FlowPanel agreementGroup = new FlowPanel();
- agreementGroup.setStyleName("gerrit-RegisterScreen-Section");
- agreementGroup.add(new SmallHeading(Util.C.welcomeAgreementHeading()));
- final HTML whyAgreement = new HTML(Util.C.welcomeAgreementText());
- whyAgreement.setStyleName("gerrit-RegisterScreen-Explain");
- agreementGroup.add(whyAgreement);
-
- choices.add(new InlineHyperlink(Util.C.newAgreement(),
- Link.SETTINGS_NEW_AGREEMENT + "," + nextToken));
- choices
- .add(new InlineHyperlink(Util.C.welcomeAgreementLater(), nextToken));
- formBody.add(agreementGroup);
- } else {
- choices.add(new InlineHyperlink(Util.C.welcomeContinue(), nextToken));
- }
- formBody.add(choices);
-
- final FormPanel form = new FormPanel();
- form.add(formBody);
- add(form);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java b/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
deleted file mode 100644
index 1b376bd99c..0000000000
--- a/src/main/java/com/google/gerrit/client/account/SshHostKeyPanel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.data.SshHostKey;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-class SshHostKeyPanel extends Composite {
- SshHostKeyPanel(final SshHostKey info) {
- final FlowPanel body = new FlowPanel();
- body.setStyleName("gerrit-SshHostKeyPanel");
- body.add(new SmallHeading(Util.C.sshHostKeyTitle()));
- {
- final Label fpLbl = new Label(Util.C.sshHostKeyFingerprint());
- fpLbl.setStyleName("gerrit-SshHostKeyPanel-Heading");
- body.add(fpLbl);
- final Label fpVal = new Label(info.getFingerprint());
- fpVal.setStyleName("gerrit-SshHostKeyPanel-FingerprintData");
- body.add(fpVal);
- }
- {
- final HTML hdr = new HTML(Util.C.sshHostKeyKnownHostEntry());
- hdr.setStyleName("gerrit-SshHostKeyPanel-Heading");
- body.add(hdr);
- final CopyableLabel lbl =
- new CopyableLabel(info.getHostIdent() + " " + info.getHostKey());
- lbl.addStyleName("gerrit-SshHostKeyPanel-KnownHostEntry");
- body.add(lbl);
- }
- initWidget(body);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/SshPanel.java b/src/main/java/com/google/gerrit/client/account/SshPanel.java
deleted file mode 100644
index 0e34617af0..0000000000
--- a/src/main/java/com/google/gerrit/client/account/SshPanel.java
+++ /dev/null
@@ -1,588 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.SshHostKey;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.InvalidSshKeyException;
-import com.google.gerrit.client.rpc.InvalidSshUserNameException;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.RootPanel;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.HashSet;
-import java.util.List;
-
-class SshPanel extends Composite {
- private static boolean loadedApplet;
- private static Element applet;
- private static String appletErrorInvalidKey;
- private static String appletErrorSecurity;
-
- private int labelIdx, fieldIdx;
-
- private NpTextBox userNameTxt;
- private Button changeUserName;
-
- private SshKeyTable keys;
-
- private Button showAddKeyBlock;
- private Panel addKeyBlock;
- private Button closeAddKeyBlock;
- private Button clearNew;
- private Button addNew;
- private Button browse;
- private NpTextArea addTxt;
- private Button delSel;
-
- private Panel serverKeys;
-
- SshPanel() {
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- labelIdx = 1;
- fieldIdx = 0;
- } else {
- labelIdx = 0;
- fieldIdx = 1;
- }
-
- final FlowPanel body = new FlowPanel();
-
- userNameTxt = new NpTextBox();
- if (Gerrit.isSignedIn()) {
- userNameTxt.setText(Gerrit.getUserAccount().getSshUserName());
- }
- userNameTxt.addKeyPressHandler(new SshUserNameValidator());
- userNameTxt.addStyleName("gerrit-SshPanel-username");
- userNameTxt.setVisibleLength(16);
- userNameTxt.setReadOnly(!canEditSshUserName());
-
- changeUserName = new Button(Util.C.buttonChangeSshUserName());
- changeUserName.setVisible(canEditSshUserName());
- changeUserName.setEnabled(false);
- changeUserName.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doChangeUserName();
- }
- });
- new TextSaveButtonListener(userNameTxt, changeUserName);
-
- final Grid userInfo = new Grid(1, 2);
- userInfo.setStyleName("gerrit-InfoBlock");
- userInfo.addStyleName("gerrit-AccountInfoBlock");
- body.add(userInfo);
-
- final FlowPanel userNameRow = new FlowPanel();
- userNameRow.add(userNameTxt);
- userNameRow.add(changeUserName);
-
- row(userInfo, 0, Util.C.sshUserName(), userNameRow);
- userInfo.getCellFormatter().addStyleName(0, 0, "topmost");
- userInfo.getCellFormatter().addStyleName(0, 0, "topmost");
- userInfo.getCellFormatter().addStyleName(0, 1, "topmost");
- userInfo.getCellFormatter().addStyleName(0, 0, "bottomheader");
-
- showAddKeyBlock = new Button(Util.C.buttonShowAddSshKey());
- showAddKeyBlock.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- showAddKeyBlock(true);
- }
- });
-
- keys = new SshKeyTable();
- body.add(keys);
- {
- final FlowPanel fp = new FlowPanel();
- delSel = new Button(Util.C.buttonDeleteSshKey());
- delSel.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- keys.deleteChecked();
- }
- });
- fp.add(delSel);
- fp.add(showAddKeyBlock);
- body.add(fp);
- }
-
- addKeyBlock = new VerticalPanel();
- addKeyBlock.setVisible(false);
- addKeyBlock.setStyleName("gerrit-AddSshKeyPanel");
- addKeyBlock.add(new SmallHeading(Util.C.addSshKeyPanelHeader()));
- addKeyBlock.add(new HTML(Util.C.addSshKeyHelp()));
-
- addTxt = new NpTextArea();
- addTxt.setVisibleLines(12);
- addTxt.setCharacterWidth(80);
- DOM.setElementPropertyBoolean(addTxt.getElement(), "spellcheck", false);
- addKeyBlock.add(addTxt);
-
- final HorizontalPanel buttons = new HorizontalPanel();
- addKeyBlock.add(buttons);
-
- clearNew = new Button(Util.C.buttonClearSshKeyInput());
- clearNew.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- addTxt.setText("");
- addTxt.setFocus(true);
- }
- });
- buttons.add(clearNew);
-
- browse = new Button(Util.C.buttonOpenSshKey());
- browse.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doBrowse();
- }
- });
- browse.setVisible(GWT.isScript() && (!loadedApplet || applet != null));
- buttons.add(browse);
-
- addNew = new Button(Util.C.buttonAddSshKey());
- addNew.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNew();
- }
- });
- buttons.add(addNew);
-
- closeAddKeyBlock = new Button(Util.C.buttonCloseAddSshKey());
- closeAddKeyBlock.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- showAddKeyBlock(false);
- }
- });
- buttons.add(closeAddKeyBlock);
- buttons.setCellWidth(closeAddKeyBlock, "100%");
- buttons.setCellHorizontalAlignment(closeAddKeyBlock,
- HasHorizontalAlignment.ALIGN_RIGHT);
-
- body.add(addKeyBlock);
-
- serverKeys = new FlowPanel();
- body.add(serverKeys);
-
- initWidget(body);
- }
-
- private boolean canEditSshUserName() {
- return Gerrit.getConfig().canEdit(Account.FieldName.SSH_USER_NAME);
- }
-
- protected void row(final Grid info, final int row, final String name,
- final Widget field) {
- info.setText(row, labelIdx, name);
- info.setWidget(row, fieldIdx, field);
- info.getCellFormatter().addStyleName(row, 0, "header");
- }
-
- void setKeyTableVisible(final boolean on) {
- keys.setVisible(on);
- delSel.setVisible(on);
- closeAddKeyBlock.setVisible(on);
- }
-
- void doChangeUserName() {
- if (!canEditSshUserName()) {
- return;
- }
-
- String newName = userNameTxt.getText();
- if ("".equals(newName)) {
- newName = null;
- }
- if (newName != null && !newName.matches(Account.SSH_USER_NAME_PATTERN)) {
- invalidUserName();
- return;
- }
-
- userNameTxt.setEnabled(false);
- changeUserName.setEnabled(false);
-
- final String newSshUserName = newName;
- Util.ACCOUNT_SEC.changeSshUserName(newSshUserName,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- userNameTxt.setEnabled(true);
- changeUserName.setEnabled(false);
- if (Gerrit.isSignedIn()) {
- Gerrit.getUserAccount().setSshUserName(newSshUserName);
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- userNameTxt.setEnabled(true);
- changeUserName.setEnabled(true);
- if (InvalidSshUserNameException.MESSAGE.equals(caught.getMessage())) {
- invalidUserName();
- } else {
- super.onFailure(caught);
- }
- }
- });
- }
-
- void invalidUserName() {
- userNameTxt.setFocus(true);
- new ErrorDialog(Util.C.invalidSshUserName()).center();
- }
-
- void doBrowse() {
- browse.setEnabled(false);
- if (!loadedApplet) {
- applet = DOM.createElement("applet");
- applet.setAttribute("code",
- "com.google.gerrit.keyapplet.ReadPublicKey.class");
- applet.setAttribute("archive", GWT.getModuleBaseURL()
- + "gerrit-keyapplet.cache.jar?v=" + Gerrit.getVersion());
- applet.setAttribute("mayscript", "true");
- applet.setAttribute("width", "0");
- applet.setAttribute("height", "0");
- RootPanel.getBodyElement().appendChild(applet);
- loadedApplet = true;
-
- // We have to defer to allow the event loop time to setup that
- // new applet tag we just created above, and actually load the
- // applet into the runtime.
- //
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- doBrowse();
- }
- });
- return;
- }
- if (applet == null) {
- // If the applet element is null, the applet was determined
- // to have failed to load, and we are dead. Hide the button.
- //
- noBrowse();
- return;
- }
-
- String txt;
- try {
- txt = openPublicKey(applet);
- } catch (RuntimeException re) {
- // If this call fails, the applet is dead. It is most likely
- // not loading due to Java support being disabled.
- //
- noBrowse();
- return;
- }
- if (txt == null) {
- txt = "";
- }
-
- browse.setEnabled(true);
-
- if (appletErrorInvalidKey == null) {
- appletErrorInvalidKey = getErrorInvalidKey(applet);
- appletErrorSecurity = getErrorSecurity(applet);
- }
-
- if (appletErrorInvalidKey.equals(txt)) {
- new ErrorDialog(Util.C.invalidSshKeyError()).center();
- return;
- }
- if (appletErrorSecurity.equals(txt)) {
- new ErrorDialog(Util.C.invalidSshKeyError()).center();
- return;
- }
-
- addTxt.setText(txt);
- addNew.setFocus(true);
- }
-
- private void noBrowse() {
- if (applet != null) {
- applet.getParentElement().removeChild(applet);
- applet = null;
- }
- browse.setVisible(false);
- new ErrorDialog(Util.C.sshJavaAppletNotAvailable()).center();
- }
-
- private static native String openPublicKey(Element keyapp)
- /*-{ var r = keyapp.openPublicKey(); return r == null ? null : ''+r; }-*/;
-
- private static native String getErrorInvalidKey(Element keyapp)
- /*-{ return ''+keyapp.getErrorInvalidKey(); }-*/;
-
- private static native String getErrorSecurity(Element keyapp)
- /*-{ return ''+keyapp.getErrorSecurity(); }-*/;
-
- void doAddNew() {
- final String txt = addTxt.getText();
- if (txt != null && txt.length() > 0) {
- addNew.setEnabled(false);
- Util.ACCOUNT_SEC.addSshKey(txt, new GerritCallback<AccountSshKey>() {
- public void onSuccess(final AccountSshKey result) {
- addNew.setEnabled(true);
- addTxt.setText("");
- keys.addOneKey(result);
- if (!keys.isVisible()) {
- showAddKeyBlock(false);
- setKeyTableVisible(true);
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addNew.setEnabled(true);
-
- if (isInvalidSshKey(caught)) {
- new ErrorDialog(Util.C.invalidSshKeyError()).center();
-
- } else {
- super.onFailure(caught);
- }
- }
-
- private boolean isInvalidSshKey(final Throwable caught) {
- if (caught instanceof InvalidSshKeyException) {
- return true;
- }
- return caught instanceof RemoteJsonException
- && InvalidSshKeyException.MESSAGE.equals(caught.getMessage());
- }
- });
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
-
- userNameTxt.setEnabled(false);
- Util.ACCOUNT_SVC.myAccount(new GerritCallback<Account>() {
- public void onSuccess(final Account result) {
- if (Gerrit.isSignedIn()) {
- Gerrit.getUserAccount().setSshUserName(result.getSshUserName());
- }
- userNameTxt.setText(result.getSshUserName());
- userNameTxt.setEnabled(true);
- }
- });
-
- Util.ACCOUNT_SEC.mySshKeys(new GerritCallback<List<AccountSshKey>>() {
- public void onSuccess(final List<AccountSshKey> result) {
- keys.display(result);
- if (result.isEmpty() && keys.isVisible()) {
- showAddKeyBlock(true);
- }
- }
- });
-
- Gerrit.SYSTEM_SVC.daemonHostKeys(new GerritCallback<List<SshHostKey>>() {
- public void onSuccess(final List<SshHostKey> result) {
- serverKeys.clear();
- for (final SshHostKey keyInfo : result) {
- serverKeys.add(new SshHostKeyPanel(keyInfo));
- }
- }
- });
- }
-
- private void showAddKeyBlock(final boolean show) {
- showAddKeyBlock.setVisible(!show);
- addKeyBlock.setVisible(show);
- }
-
- private final class SshUserNameValidator implements KeyPressHandler {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- final char code = event.getCharCode();
- switch (code) {
- case KeyCodes.KEY_ALT:
- case KeyCodes.KEY_BACKSPACE:
- case KeyCodes.KEY_CTRL:
- case KeyCodes.KEY_DELETE:
- case KeyCodes.KEY_DOWN:
- case KeyCodes.KEY_END:
- case KeyCodes.KEY_ENTER:
- case KeyCodes.KEY_ESCAPE:
- case KeyCodes.KEY_HOME:
- case KeyCodes.KEY_LEFT:
- case KeyCodes.KEY_PAGEDOWN:
- case KeyCodes.KEY_PAGEUP:
- case KeyCodes.KEY_RIGHT:
- case KeyCodes.KEY_SHIFT:
- case KeyCodes.KEY_TAB:
- case KeyCodes.KEY_UP:
- // Allow these, even if one of their assigned codes is
- // identical to an ASCII character we do not want to
- // allow in the box.
- //
- // We still want to let the user move around the input box
- // with their arrow keys, or to move between fields using tab.
- // Invalid characters introduced will be caught through the
- // server's own validation of the input data.
- //
- break;
-
- default:
- final TextBox box = (TextBox) event.getSource();
- final String re;
- if (box.getCursorPos() == 0)
- re = Account.SSH_USER_NAME_PATTERN_FIRST;
- else
- re = Account.SSH_USER_NAME_PATTERN_REST;
- if (!String.valueOf(code).matches("^" + re + "$")) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
-
- private class SshKeyTable extends FancyFlexTable<AccountSshKey> {
- private static final String S_INVALID = "gerrit-SshKeyPanel-Invalid";
-
- SshKeyTable() {
- table.setWidth("");
- table.setText(0, 3, Util.C.sshKeyAlgorithm());
- table.setText(0, 4, Util.C.sshKeyKey());
- table.setText(0, 5, Util.C.sshKeyComment());
- table.setText(0, 6, Util.C.sshKeyLastUsed());
- table.setText(0, 7, Util.C.sshKeyStored());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- fmt.addStyleName(0, 4, S_DATA_HEADER);
- fmt.addStyleName(0, 5, S_DATA_HEADER);
- fmt.addStyleName(0, 6, S_DATA_HEADER);
- fmt.addStyleName(0, 7, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<AccountSshKey.Id> ids = new HashSet<AccountSshKey.Id>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountSshKey k = getRowItem(row);
- if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(k.getKey());
- }
- }
- if (!ids.isEmpty()) {
- Util.ACCOUNT_SEC.deleteSshKeys(ids, new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- for (int row = 1; row < table.getRowCount();) {
- final AccountSshKey k = getRowItem(row);
- if (k != null && ids.contains(k.getKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- if (table.getRowCount() == 1) {
- showAddKeyBlock(true);
- }
- }
- });
- }
- }
-
- void display(final List<AccountSshKey> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountSshKey k : result) {
- addOneKey(k);
- }
- }
-
- void addOneKey(final AccountSshKey k) {
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
-
- table.setWidget(row, 1, new CheckBox());
- if (k.isValid()) {
- table.setText(row, 2, "");
- fmt.removeStyleName(row, 2, S_INVALID);
- } else {
- table.setText(row, 2, Util.C.sshKeyInvalid());
- fmt.addStyleName(row, 2, S_INVALID);
- }
- table.setText(row, 3, k.getAlgorithm());
- table.setText(row, 4, elide(k.getEncodedKey()));
- table.setText(row, 5, k.getComment());
- table.setText(row, 6, FormatUtil.mediumFormat(k.getLastUsedOn()));
- table.setText(row, 7, FormatUtil.mediumFormat(k.getStoredOn()));
-
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_ICON_CELL);
- fmt.addStyleName(row, 4, "gerrit-SshKeyPanel-EncodedKey");
- for (int c = 3; c <= 7; c++) {
- fmt.addStyleName(row, c, S_DATA_CELL);
- }
- fmt.addStyleName(row, 6, "C_LAST_UPDATE");
- fmt.addStyleName(row, 7, "C_LAST_UPDATE");
-
- setRowItem(row, k);
- }
-
- String elide(final String s) {
- if (s == null || s.length() < 40) {
- return s;
- }
- return s.substring(0, 30) + "..." + s.substring(s.length() - 10);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/Util.java b/src/main/java/com/google/gerrit/client/account/Util.java
deleted file mode 100644
index 0f50b0e9c9..0000000000
--- a/src/main/java/com/google/gerrit/client/account/Util.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final AccountConstants C = GWT.create(AccountConstants.class);
- public static final AccountMessages M = GWT.create(AccountMessages.class);
- public static final AccountService ACCOUNT_SVC;
- public static final AccountSecurity ACCOUNT_SEC;
-
- static {
- ACCOUNT_SVC = GWT.create(AccountService.class);
- JsonUtil.bind(ACCOUNT_SVC, "rpc/AccountService");
-
- ACCOUNT_SEC = GWT.create(AccountSecurity.class);
- JsonUtil.bind(ACCOUNT_SEC, "rpc/AccountSecurity");
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java b/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
deleted file mode 100644
index 6acdd37e35..0000000000
--- a/src/main/java/com/google/gerrit/client/account/ValidateEmailScreen.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.account;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gwt.user.client.History;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-public class ValidateEmailScreen extends AccountScreen {
- private final String magicToken;
-
- public ValidateEmailScreen(final String magicToken) {
- this.magicToken = magicToken;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.accountSettingsHeading());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.ACCOUNT_SEC.validateEmail(magicToken,
- new ScreenLoadCallback<VoidResult>(this) {
- @Override
- protected void preDisplay(final VoidResult result) {
- }
-
- @Override
- protected void postDisplay() {
- History.newItem(Link.SETTINGS_CONTACT, true);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
deleted file mode 100644
index fedf1f4590..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
+++ /dev/null
@@ -1,533 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountDashboardLink;
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.AddMemberBox;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.HashSet;
-import java.util.List;
-
-public class AccountGroupScreen extends AccountScreen {
- private final AccountGroup.Id groupId;
- private AccountInfoCache accounts = AccountInfoCache.empty();
- private MemberTable members;
-
- private NpTextBox groupNameTxt;
- private Button saveName;
-
- private NpTextBox ownerTxtBox;
- private SuggestBox ownerTxt;
- private Button saveOwner;
-
- private NpTextArea descTxt;
- private Button saveDesc;
-
- private Label typeSystem;
- private ListBox typeSelect;
- private Button saveType;
-
- private Panel memberPanel;
- private AddMemberBox addMemberBox;
- private Button delMember;
-
- private Panel externalPanel;
- private Label externalName;
- private NpTextBox externalNameFilter;
- private Button externalNameSearch;
- private Grid externalMatches;
-
- public AccountGroupScreen(final AccountGroup.Id toShow) {
- groupId = toShow;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>(
- this) {
- @Override
- protected void preDisplay(final GroupDetail result) {
- display(result);
- }
- });
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- initName();
- initOwner();
- initDescription();
- initGroupType();
- initMemberList();
- initExternal();
- }
-
- private void initName() {
- final VerticalPanel groupNamePanel = new VerticalPanel();
- groupNameTxt = new NpTextBox();
- groupNameTxt.setVisibleLength(60);
- groupNamePanel.add(groupNameTxt);
-
- saveName = new Button(Util.C.buttonRenameGroup());
- saveName.setEnabled(false);
- saveName.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final String newName = groupNameTxt.getText().trim();
- Util.GROUP_SVC.renameGroup(groupId, newName,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- saveName.setEnabled(false);
- setPageTitle(Util.M.group(newName));
- }
- });
- }
- });
- groupNamePanel.add(saveName);
- add(groupNamePanel);
-
- new TextSaveButtonListener(groupNameTxt, saveName);
- }
-
- private void initOwner() {
- final VerticalPanel ownerPanel = new VerticalPanel();
- ownerPanel.add(new SmallHeading(Util.C.headingOwner()));
-
- ownerTxtBox = new NpTextBox();
- ownerTxtBox.setVisibleLength(60);
- ownerTxt = new SuggestBox(new AccountGroupSuggestOracle(), ownerTxtBox);
- ownerPanel.add(ownerTxt);
-
- saveOwner = new Button(Util.C.buttonChangeGroupOwner());
- saveOwner.setEnabled(false);
- saveOwner.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final String newOwner = ownerTxt.getText().trim();
- if (newOwner.length() > 0) {
- Util.GROUP_SVC.changeGroupOwner(groupId, newOwner,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- saveOwner.setEnabled(false);
- }
- });
- }
- }
- });
- ownerPanel.add(saveOwner);
- add(ownerPanel);
-
- new TextSaveButtonListener(ownerTxtBox, saveOwner);
- }
-
- private void initDescription() {
- final VerticalPanel vp = new VerticalPanel();
- vp.add(new SmallHeading(Util.C.headingDescription()));
-
- descTxt = new NpTextArea();
- descTxt.setVisibleLines(6);
- descTxt.setCharacterWidth(60);
- vp.add(descTxt);
-
- saveDesc = new Button(Util.C.buttonSaveDescription());
- saveDesc.setEnabled(false);
- saveDesc.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final String txt = descTxt.getText().trim();
- Util.GROUP_SVC.changeGroupDescription(groupId, txt,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- saveDesc.setEnabled(false);
- }
- });
- }
- });
- vp.add(saveDesc);
- add(vp);
-
- new TextSaveButtonListener(descTxt, saveDesc);
- }
-
- private void initGroupType() {
- typeSystem = new Label(Util.C.groupType_SYSTEM());
-
- typeSelect = new ListBox();
- typeSelect.addItem(Util.C.groupType_INTERNAL(), AccountGroup.Type.INTERNAL.name());
- typeSelect.addItem(Util.C.groupType_LDAP(), AccountGroup.Type.LDAP.name());
- typeSelect.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(ChangeEvent event) {
- saveType.setEnabled(true);
- }
- });
-
- saveType = new Button(Util.C.buttonChangeGroupType());
- saveType.setEnabled(false);
- saveType.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- onSaveType();
- }
- });
-
- final VerticalPanel fp = new VerticalPanel();
- fp.add(new SmallHeading(Util.C.headingGroupType()));
- fp.add(typeSystem);
- fp.add(typeSelect);
- fp.add(saveType);
- add(fp);
- }
-
- private void initMemberList() {
- addMemberBox = new AddMemberBox();
-
- addMemberBox.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNew();
- }
- });
-
- members = new MemberTable();
-
- delMember = new Button(Util.C.buttonDeleteGroupMembers());
- delMember.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- members.deleteChecked();
- }
- });
-
- memberPanel = new FlowPanel();
- memberPanel.add(new SmallHeading(Util.C.headingMembers()));
- memberPanel.add(addMemberBox);
- memberPanel.add(members);
- memberPanel.add(delMember);
- add(memberPanel);
- }
-
- private void initExternal() {
- externalName = new Label();
-
- externalNameFilter = new NpTextBox();
- externalNameFilter.setVisibleLength(30);
- externalNameFilter.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getCharCode() == KeyCodes.KEY_ENTER) {
- doExternalSearch();
- }
- }
- });
-
- externalNameSearch = new Button(Gerrit.C.searchButton());
- externalNameSearch.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doExternalSearch();
- }
- });
-
- externalMatches = new Grid();
- externalMatches.setStyleName("gerrit-InfoTable");
- externalMatches.setVisible(false);
-
- final FlowPanel searchLine = new FlowPanel();
- searchLine.add(externalNameFilter);
- searchLine.add(externalNameSearch);
-
- externalPanel = new VerticalPanel();
- externalPanel.add(new SmallHeading(Util.C.headingExternalGroup()));
- externalPanel.add(externalName);
- externalPanel.add(searchLine);
- externalPanel.add(externalMatches);
- add(externalPanel);
- }
-
- private void setType(final AccountGroup.Type newType) {
- final boolean system = newType == AccountGroup.Type.SYSTEM;
-
- typeSystem.setVisible(system);
- typeSelect.setVisible(!system);
- saveType.setVisible(!system);
- memberPanel.setVisible(newType == AccountGroup.Type.INTERNAL);
- externalPanel.setVisible(newType == AccountGroup.Type.LDAP);
- externalNameFilter.setText(groupNameTxt.getText());
-
- if (!system) {
- for (int i = 0; i < typeSelect.getItemCount(); i++) {
- if (newType.name().equals(typeSelect.getValue(i))) {
- typeSelect.setSelectedIndex(i);
- break;
- }
- }
- }
-
- saveType.setEnabled(false);
- }
-
- private void onSaveType() {
- final int idx = typeSelect.getSelectedIndex();
- final AccountGroup.Type newType =
- AccountGroup.Type.valueOf(typeSelect.getValue(idx));
-
- typeSelect.setEnabled(false);
- saveType.setEnabled(false);
-
- Util.GROUP_SVC.changeGroupType(groupId, newType,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- typeSelect.setEnabled(true);
- setType(newType);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- typeSelect.setEnabled(true);
- saveType.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void doExternalSearch() {
- externalNameFilter.setEnabled(false);
- externalNameSearch.setEnabled(false);
- Util.GROUP_SVC.searchExternalGroups(externalNameFilter.getText(),
- new GerritCallback<List<AccountGroup.ExternalNameKey>>() {
- @Override
- public void onSuccess(List<AccountGroup.ExternalNameKey> result) {
- final CellFormatter fmt = externalMatches.getCellFormatter();
-
- if (result.isEmpty()) {
- externalMatches.resize(1, 1);
- externalMatches.setText(0, 0, Util.C.errorNoMatchingGroups());
- fmt.setStyleName(0, 0, "header");
- return;
- }
-
- externalMatches.resize(1 + result.size(), 2);
-
- externalMatches.setText(0, 0, Util.C.columnGroupName());
- externalMatches.setText(0, 1, "");
- fmt.setStyleName(0, 0, "header");
- fmt.setStyleName(0, 1, "header");
-
- for (int row = 0; row < result.size(); row++) {
- final AccountGroup.ExternalNameKey key = result.get(row);
- final Button b = new Button(Util.C.buttonSelectGroup());
- b.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setExternalGroup(key);
- }
- });
- externalMatches.setText(1 + row, 0, key.get());
- externalMatches.setWidget(1 + row, 1, b);
- fmt.setStyleName(1 + row, 1, "rightmost");
- }
- externalMatches.setVisible(true);
-
- externalNameFilter.setEnabled(true);
- externalNameSearch.setEnabled(true);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- externalNameFilter.setEnabled(true);
- externalNameSearch.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void setExternalGroup(final AccountGroup.ExternalNameKey key) {
- externalMatches.setVisible(false);
-
- Util.GROUP_SVC.changeExternalGroup(groupId, key,
- new GerritCallback<VoidResult>() {
- @Override
- public void onSuccess(VoidResult result) {
- externalName.setText(key.get());
- }
-
- @Override
- public void onFailure(Throwable caught) {
- externalMatches.setVisible(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void display(final GroupDetail result) {
- final AccountGroup group = result.group;
- setPageTitle(Util.M.group(group.getName()));
- groupNameTxt.setText(group.getName());
- if (result.ownerGroup != null) {
- ownerTxt.setText(result.ownerGroup.getName());
- } else {
- ownerTxt.setText(Util.M.deletedGroup(group.getOwnerGroupId().get()));
- }
- descTxt.setText(group.getDescription());
-
- switch (group.getType()) {
- case INTERNAL:
- accounts = result.accounts;
- members.display(result.members);
- break;
-
- case LDAP:
- externalName.setText(group.getExternalNameKey() != null ? group
- .getExternalNameKey().get() : Util.C.noGroupSelected());
- break;
- }
-
- setType(group.getType());
- }
-
- void doAddNew() {
- final String nameEmail = addMemberBox.getText();
- if (nameEmail.length() == 0) {
- return;
- }
-
- addMemberBox.setEnabled(false);
- Util.GROUP_SVC.addGroupMember(groupId, nameEmail,
- new GerritCallback<GroupDetail>() {
- public void onSuccess(final GroupDetail result) {
- addMemberBox.setEnabled(true);
- addMemberBox.setText("");
- if (result.accounts != null && result.members != null) {
- accounts.merge(result.accounts);
- members.display(result.members);
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addMemberBox.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private class MemberTable extends FancyFlexTable<AccountGroupMember> {
- MemberTable() {
- table.setText(0, 2, Util.C.columnMember());
- table.setText(0, 3, Util.C.columnEmailAddress());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<AccountGroupMember.Key> ids =
- new HashSet<AccountGroupMember.Key>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final AccountGroupMember k = getRowItem(row);
- if (k != null && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(k.getKey());
- }
- }
- if (!ids.isEmpty()) {
- Util.GROUP_SVC.deleteGroupMembers(groupId, ids,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- for (int row = 1; row < table.getRowCount();) {
- final AccountGroupMember k = getRowItem(row);
- if (k != null && ids.contains(k.getKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
- }
-
- void insertMember(final AccountGroupMember k) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
-
- void display(final List<AccountGroupMember> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountGroupMember k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(final int row, final AccountGroupMember k) {
- final Account.Id accountId = k.getAccountId();
- table.setWidget(row, 1, new CheckBox());
- table.setWidget(row, 2, AccountDashboardLink.link(accounts, accountId));
- table.setText(row, 3, accounts.get(accountId).getPreferredEmail());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_DATA_CELL);
- fmt.addStyleName(row, 3, S_DATA_CELL);
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/GroupAdminService.java b/src/main/java/com/google/gerrit/client/admin/GroupAdminService.java
deleted file mode 100644
index 4df860c3d8..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/GroupAdminService.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.List;
-import java.util.Set;
-
-public interface GroupAdminService extends RemoteJsonService {
- @SignInRequired
- void ownedGroups(AsyncCallback<List<AccountGroup>> callback);
-
- @SignInRequired
- void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback);
-
- @SignInRequired
- void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback);
-
- @SignInRequired
- void changeGroupDescription(AccountGroup.Id groupId, String description,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void changeGroupOwner(AccountGroup.Id groupId, String newOwnerName,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void renameGroup(AccountGroup.Id groupId, String newName,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void changeGroupType(AccountGroup.Id groupId, AccountGroup.Type newType,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void changeExternalGroup(AccountGroup.Id groupId,
- AccountGroup.ExternalNameKey bindTo, AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void searchExternalGroups(String searchFilter,
- AsyncCallback<List<AccountGroup.ExternalNameKey>> callback);
-
- @SignInRequired
- void addGroupMember(AccountGroup.Id groupId, String nameOrEmail,
- AsyncCallback<GroupDetail> callback);
-
- @SignInRequired
- void deleteGroupMembers(AccountGroup.Id groupId,
- Set<AccountGroupMember.Key> keys, AsyncCallback<VoidResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/GroupDetail.java b/src/main/java/com/google/gerrit/client/admin/GroupDetail.java
deleted file mode 100644
index c73a72a899..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/GroupDetail.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-
-import java.util.List;
-
-public class GroupDetail {
- protected AccountInfoCache accounts;
- protected AccountGroup group;
- protected List<AccountGroupMember> members;
- protected AccountGroup ownerGroup;
-
- public GroupDetail() {
- }
-
- public void setAccounts(AccountInfoCache c) {
- accounts = c;
- }
-
- public void setGroup(AccountGroup g) {
- group = g;
- }
-
- public void setMembers(List<AccountGroupMember> m) {
- members = m;
- }
-
- public void setOwnerGroup(AccountGroup g) {
- ownerGroup = g;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java b/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
deleted file mode 100644
index 5bd5a87043..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-import java.util.List;
-
-public class GroupListScreen extends AccountScreen {
- private GroupTable groups;
-
- private NpTextBox addTxt;
- private Button addNew;
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.GROUP_SVC
- .ownedGroups(new ScreenLoadCallback<List<AccountGroup>>(this) {
- @Override
- protected void preDisplay(final List<AccountGroup> result) {
- groups.display(result);
- groups.finishDisplay();
- }
- });
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.groupListTitle());
-
- groups = new GroupTable(true /* hyperlink to admin */, Link.ADMIN_GROUPS);
- add(groups);
-
- final VerticalPanel fp = new VerticalPanel();
- fp.setStyleName("gerrit-AddSshKeyPanel");
- fp.add(new SmallHeading(Util.C.headingCreateGroup()));
-
- addTxt = new NpTextBox();
- addTxt.setVisibleLength(60);
- fp.add(addTxt);
-
- addNew = new Button(Util.C.buttonCreateGroup());
- addNew.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doCreateGroup();
- }
- });
- fp.add(addNew);
- add(fp);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- groups.setRegisterKeys(true);
- }
-
- private void doCreateGroup() {
- final String newName = addTxt.getText();
- if (newName == null || newName.length() == 0) {
- return;
- }
-
- Util.GROUP_SVC.createGroup(newName, new GerritCallback<AccountGroup.Id>() {
- public void onSuccess(final AccountGroup.Id result) {
- History.newItem(Link.toAccountGroup(result));
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/GroupTable.java b/src/main/java/com/google/gerrit/client/admin/GroupTable.java
deleted file mode 100644
index 613a4056af..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/GroupTable.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Hyperlink;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-
-import java.util.List;
-
-
-public class GroupTable extends NavigationTable<AccountGroup> {
- private final boolean enableLink;
-
- public GroupTable(final boolean enableLink) {
- this(enableLink, null);
- }
-
- public GroupTable(final boolean enableLink, final String pointerId) {
- this.enableLink = enableLink;
-
- setSavePointerId(pointerId);
- keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.groupListPrev()));
- keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.groupListNext()));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.groupListOpen()));
- keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
- .groupListOpen()));
-
- table.setText(0, 1, Util.C.columnGroupName());
- table.setText(0, 2, Util.C.columnGroupDescription());
- table.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell != null && cell.getCellIndex() != 1
- && getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_DATA_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- }
-
- @Override
- protected Object getRowItemKey(final AccountGroup item) {
- return item.getId();
- }
-
- @Override
- protected void onOpenRow(final int row) {
- History.newItem(Link.toAccountGroup(getRowItem(row).getId()));
- }
-
- public void display(final List<AccountGroup> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final AccountGroup k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(final int row, final AccountGroup k) {
- if (enableLink) {
- table.setWidget(row, 1, new Hyperlink(k.getName(), Link.toAccountGroup(k
- .getId())));
- } else {
- table.setText(row, 1, k.getName());
- }
- table.setText(row, 2, k.getDescription());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_DATA_CELL);
- fmt.addStyleName(row, 1, "GroupName");
- fmt.addStyleName(row, 2, S_DATA_CELL);
-
- setRowItem(row, k);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java b/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java
deleted file mode 100644
index 9a02778733..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectAdminScreen.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gwt.event.logical.shared.SelectionEvent;
-import com.google.gwt.event.logical.shared.SelectionHandler;
-import com.google.gwt.user.client.ui.LazyPanel;
-import com.google.gwt.user.client.ui.TabPanel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ProjectAdminScreen extends AccountScreen {
- static final String INFO_TAB = "info";
- static final String BRANCH_TAB = "branches";
- static final String ACCESS_TAB = "access";
-
- private final Project.NameKey projectName;
- private final String initialTabToken;
-
- private List<String> tabTokens;
- private TabPanel tabs;
-
- public ProjectAdminScreen(final Project.NameKey toShow, final String token) {
- projectName = toShow;
- initialTabToken = token;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.PROJECT_SVC.projectDetail(projectName,
- new ScreenLoadCallback<ProjectDetail>(this) {
- @Override
- protected void preDisplay(final ProjectDetail result) {
- display(result);
- tabs.selectTab(tabTokens.indexOf(initialTabToken));
- }
- });
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- tabTokens = new ArrayList<String>();
- tabs = new TabPanel();
- tabs.setWidth("98%");
- add(tabs);
-
- tabs.add(new LazyPanel() {
- @Override
- protected ProjectInfoPanel createWidget() {
- return new ProjectInfoPanel(projectName);
- }
- }, Util.C.projectAdminTabGeneral());
- tabTokens.add(Link.toProjectAdmin(projectName, INFO_TAB));
-
- if (!Gerrit.getConfig().getWildProject().equals(projectName)) {
- tabs.add(new LazyPanel() {
- @Override
- protected ProjectBranchesPanel createWidget() {
- return new ProjectBranchesPanel(projectName);
- }
- }, Util.C.projectAdminTabBranches());
- tabTokens.add(Link.toProjectAdmin(projectName, BRANCH_TAB));
- }
-
- tabs.add(new LazyPanel() {
- @Override
- protected ProjectRightsPanel createWidget() {
- return new ProjectRightsPanel(projectName);
- }
- }, Util.C.projectAdminTabAccess());
- tabTokens.add(Link.toProjectAdmin(projectName, ACCESS_TAB));
-
- tabs.addSelectionHandler(new SelectionHandler<Integer>() {
- @Override
- public void onSelection(final SelectionEvent<Integer> event) {
- Gerrit.display(tabTokens.get(event.getSelectedItem()), false);
- }
- });
- }
-
-
- private void display(final ProjectDetail result) {
- final Project project = result.project;
- setPageTitle(Util.M.project(project.getName()));
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java b/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
deleted file mode 100644
index 969c7d99be..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectAdminService.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.List;
-import java.util.Set;
-
-public interface ProjectAdminService extends RemoteJsonService {
- @SignInRequired
- void ownedProjects(AsyncCallback<List<Project>> callback);
-
- @SignInRequired
- void projectDetail(Project.NameKey projectName,
- AsyncCallback<ProjectDetail> callback);
-
- @SignInRequired
- void changeProjectSettings(Project update,
- AsyncCallback<ProjectDetail> callback);
-
- @SignInRequired
- void deleteRight(Project.NameKey projectName, Set<ProjectRight.Key> ids,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void addRight(Project.NameKey projectName, ApprovalCategory.Id categoryId,
- String groupName, short min, short max,
- AsyncCallback<ProjectDetail> callback);
-
- @SignInRequired
- void listBranches(Project.NameKey projectName,
- AsyncCallback<List<Branch>> callback);
-
- @SignInRequired
- void addBranch(Project.NameKey projectName, String branchName,
- String startingRevision, AsyncCallback<List<Branch>> callback);
-
- @SignInRequired
- void deleteBranch(Project.NameKey projectName, Set<Branch.NameKey> ids,
- AsyncCallback<Set<Branch.NameKey>> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java
deleted file mode 100644
index c3fbe50b0a..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectBranchesPanel.java
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.GitwebLink;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.InvalidNameException;
-import com.google.gerrit.client.rpc.InvalidRevisionException;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ProjectBranchesPanel extends Composite {
- private Project.NameKey projectName;
-
- private BranchesTable branches;
- private Button delBranch;
- private Button addBranch;
- private NpTextBox nameTxtBox;
- private NpTextBox irevTxtBox;
-
- public ProjectBranchesPanel(final Project.NameKey toShow) {
- final FlowPanel body = new FlowPanel();
- initBranches(body);
- initWidget(body);
-
- projectName = toShow;
- }
-
- @Override
- protected void onLoad() {
- enableForm(false);
- super.onLoad();
-
- Util.PROJECT_SVC.listBranches(projectName,
- new GerritCallback<List<Branch>>() {
- public void onSuccess(final List<Branch> result) {
- enableForm(true);
- branches.display(result);
- }
- });
- }
-
- private void enableForm(final boolean on) {
- delBranch.setEnabled(on);
- addBranch.setEnabled(on);
- nameTxtBox.setEnabled(on);
- irevTxtBox.setEnabled(on);
- }
-
- private void initBranches(final Panel body) {
- final FlowPanel addPanel = new FlowPanel();
- addPanel.setStyleName("gerrit-AddSshKeyPanel");
-
- final Grid addGrid = new Grid(2, 2);
-
- nameTxtBox = new NpTextBox();
- nameTxtBox.setVisibleLength(50);
- nameTxtBox.setText(Util.C.defaultBranchName());
- nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- nameTxtBox.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- if (Util.C.defaultBranchName().equals(nameTxtBox.getText())) {
- nameTxtBox.setText("");
- nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- nameTxtBox.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- if ("".equals(nameTxtBox.getText())) {
- nameTxtBox.setText(Util.C.defaultBranchName());
- nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- addGrid.setText(0, 0, Util.C.columnBranchName() + ":");
- addGrid.setWidget(0, 1, nameTxtBox);
-
- irevTxtBox = new NpTextBox();
- irevTxtBox.setVisibleLength(50);
- irevTxtBox.setText(Util.C.defaultRevisionSpec());
- irevTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- irevTxtBox.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- if (Util.C.defaultRevisionSpec().equals(irevTxtBox.getText())) {
- irevTxtBox.setText("");
- irevTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- irevTxtBox.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- if ("".equals(irevTxtBox.getText())) {
- irevTxtBox.setText(Util.C.defaultRevisionSpec());
- irevTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- addGrid.setText(1, 0, Util.C.initialRevision() + ":");
- addGrid.setWidget(1, 1, irevTxtBox);
-
- addBranch = new Button(Util.C.buttonAddBranch());
- addBranch.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNewBranch();
- }
- });
- addPanel.add(addGrid);
- addPanel.add(addBranch);
-
- branches = new BranchesTable();
-
- delBranch = new Button(Util.C.buttonDeleteBranch());
- delBranch.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- branches.deleteChecked();
- }
- });
-
- body.add(branches);
- body.add(delBranch);
- body.add(addPanel);
- }
-
- private void doAddNewBranch() {
- String branchName = nameTxtBox.getText();
- if ("".equals(branchName) || Util.C.defaultBranchName().equals(branchName)) {
- return;
- }
-
- String rev = irevTxtBox.getText();
- if ("".equals(rev) || Util.C.defaultRevisionSpec().equals(rev)) {
- return;
- }
-
- if (!branchName.startsWith(Branch.R_REFS)) {
- branchName = Branch.R_HEADS + branchName;
- }
-
- addBranch.setEnabled(false);
- Util.PROJECT_SVC.addBranch(projectName, branchName, rev,
- new GerritCallback<List<Branch>>() {
- public void onSuccess(final List<Branch> result) {
- addBranch.setEnabled(true);
- nameTxtBox.setText("");
- irevTxtBox.setText("");
- branches.display(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- if (caught instanceof InvalidNameException
- || caught instanceof RemoteJsonException
- && caught.getMessage().equals(InvalidNameException.MESSAGE)) {
- nameTxtBox.selectAll();
- nameTxtBox.setFocus(true);
-
- } else if (caught instanceof InvalidRevisionException
- || caught instanceof RemoteJsonException
- && caught.getMessage().equals(InvalidRevisionException.MESSAGE)) {
- irevTxtBox.selectAll();
- irevTxtBox.setFocus(true);
- }
-
- addBranch.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private class BranchesTable extends FancyFlexTable<Branch> {
- BranchesTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.columnBranchName());
- table.setText(0, 3, Util.C.columnBranchRevision());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- fmt.addStyleName(0, 4, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<Branch.NameKey> ids = new HashSet<Branch.NameKey>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final Branch k = getRowItem(row);
- if (k != null && table.getWidget(row, 1) instanceof CheckBox
- && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(k.getNameKey());
- }
- }
- if (ids.isEmpty()) {
- return;
- }
-
- Util.PROJECT_SVC.deleteBranch(projectName, ids,
- new GerritCallback<Set<Branch.NameKey>>() {
- public void onSuccess(final Set<Branch.NameKey> deleted) {
- for (int row = 1; row < table.getRowCount();) {
- final Branch k = getRowItem(row);
- if (k != null && deleted.contains(k.getNameKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
-
- void display(final List<Branch> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final Branch k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(final int row, final Branch k) {
- final GitwebLink c = Gerrit.getConfig().getGitwebLink();
-
- table.setWidget(row, 1, new CheckBox());
- table.setText(row, 2, k.getShortName());
-
- if (k.getRevision() != null) {
- table.setText(row, 3, k.getRevision().get());
- }
-
- if (c != null) {
- table.setWidget(row, 4, new Anchor("(gitweb)", false, c.toBranch(k
- .getNameKey())));
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_DATA_CELL);
- fmt.addStyleName(row, 3, S_DATA_CELL);
- fmt.addStyleName(row, 4, S_DATA_CELL);
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java b/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java
deleted file mode 100644
index 037e270a96..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectDetail.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-
-import java.util.List;
-import java.util.Map;
-
-public class ProjectDetail {
- protected Project project;
- protected Map<AccountGroup.Id, AccountGroup> groups;
- protected List<ProjectRight> rights;
-
- public ProjectDetail() {
- }
-
- public void setProject(final Project p) {
- project = p;
- }
-
- public void setGroups(final Map<AccountGroup.Id, AccountGroup> g) {
- groups = g;
- }
-
- public void setRights(final List<ProjectRight> r) {
- rights = r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
deleted file mode 100644
index cc2b3fee33..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectInfoPanel.java
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gerrit.client.ui.TextSaveButtonListener;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-
-public class ProjectInfoPanel extends Composite {
- private Project.NameKey projectName;
- private Project project;
-
- private Panel submitTypePanel;
- private ListBox submitType;
-
- private Panel agreementsPanel;
- private CheckBox useContributorAgreements;
- private CheckBox useSignedOffBy;
-
- private NpTextArea descTxt;
- private Button saveProject;
-
- public ProjectInfoPanel(final Project.NameKey toShow) {
- saveProject = new Button(Util.C.buttonSaveChanges());
- saveProject.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- doSave();
- }
- });
-
- final FlowPanel body = new FlowPanel();
- initDescription(body);
- initSubmitType(body);
- initAgreements(body);
- body.add(saveProject);
-
- initWidget(body);
- projectName = toShow;
- }
-
- @Override
- protected void onLoad() {
- enableForm(false);
- saveProject.setEnabled(false);
- super.onLoad();
- refresh();
- }
-
- private void refresh() {
- Util.PROJECT_SVC.projectDetail(projectName,
- new GerritCallback<ProjectDetail>() {
- public void onSuccess(final ProjectDetail result) {
- enableForm(true);
- saveProject.setEnabled(false);
- display(result);
- }
- });
- }
-
- private void enableForm(final boolean on) {
- submitType.setEnabled(on);
- descTxt.setEnabled(on);
- useContributorAgreements.setEnabled(on);
- useSignedOffBy.setEnabled(on);
- }
-
- private void initDescription(final Panel body) {
- final VerticalPanel vp = new VerticalPanel();
- vp.add(new SmallHeading(Util.C.headingDescription()));
-
- descTxt = new NpTextArea();
- descTxt.setVisibleLines(6);
- descTxt.setCharacterWidth(60);
- vp.add(descTxt);
-
- body.add(vp);
- new TextSaveButtonListener(descTxt, saveProject);
- }
-
- private void initSubmitType(final Panel body) {
- submitTypePanel = new VerticalPanel();
- submitTypePanel.add(new SmallHeading(Util.C.headingSubmitType()));
-
- submitType = new ListBox();
- for (final Project.SubmitType type : Project.SubmitType.values()) {
- submitType.addItem(Util.toLongString(type), type.name());
- }
- submitType.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(final ChangeEvent event) {
- saveProject.setEnabled(true);
- }
- });
- submitTypePanel.add(submitType);
- body.add(submitTypePanel);
- }
-
- private void initAgreements(final Panel body) {
- final ValueChangeHandler<Boolean> onChangeSave =
- new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- saveProject.setEnabled(true);
- }
- };
-
- agreementsPanel = new VerticalPanel();
- agreementsPanel.add(new SmallHeading(Util.C.headingAgreements()));
-
- useContributorAgreements = new CheckBox(Util.C.useContributorAgreements());
- useContributorAgreements.addValueChangeHandler(onChangeSave);
- agreementsPanel.add(useContributorAgreements);
-
- useSignedOffBy = new CheckBox(Util.C.useSignedOffBy(), true);
- useSignedOffBy.addValueChangeHandler(onChangeSave);
- agreementsPanel.add(useSignedOffBy);
-
- body.add(agreementsPanel);
- }
-
- private void setSubmitType(final Project.SubmitType newSubmitType) {
- if (submitType != null) {
- for (int i = 0; i < submitType.getItemCount(); i++) {
- if (newSubmitType.name().equals(submitType.getValue(i))) {
- submitType.setSelectedIndex(i);
- return;
- }
- }
- submitType.setSelectedIndex(-1);
- }
- }
-
- void display(final ProjectDetail result) {
- project = result.project;
-
- final boolean isall =
- Gerrit.getConfig().getWildProject().equals(project.getNameKey());
- submitTypePanel.setVisible(!isall);
- agreementsPanel.setVisible(!isall);
- useContributorAgreements.setVisible(Gerrit.getConfig()
- .isUseContributorAgreements());
-
- descTxt.setText(project.getDescription());
- useContributorAgreements.setValue(project.isUseContributorAgreements());
- useSignedOffBy.setValue(project.isUseSignedOffBy());
- setSubmitType(project.getSubmitType());
- }
-
- private void doSave() {
- project.setDescription(descTxt.getText().trim());
- project.setUseContributorAgreements(useContributorAgreements.getValue());
- project.setUseSignedOffBy(useSignedOffBy.getValue());
- if (submitType.getSelectedIndex() >= 0) {
- project.setSubmitType(Project.SubmitType.valueOf(submitType
- .getValue(submitType.getSelectedIndex())));
- }
-
- enableForm(false);
- saveProject.setEnabled(false);
-
- Util.PROJECT_SVC.changeProjectSettings(project,
- new GerritCallback<ProjectDetail>() {
- public void onSuccess(final ProjectDetail result) {
- enableForm(true);
- display(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- refresh();
- super.onFailure(caught);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
deleted file mode 100644
index bfec747061..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.Hyperlink;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-
-import java.util.List;
-
-public class ProjectListScreen extends AccountScreen {
- private ProjectTable projects;
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.PROJECT_SVC.ownedProjects(new ScreenLoadCallback<List<Project>>(this) {
- @Override
- protected void preDisplay(final List<Project> result) {
- projects.display(result);
- projects.finishDisplay();
- }
- });
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.C.projectListTitle());
-
- projects = new ProjectTable();
- add(projects);
-
- final VerticalPanel fp = new VerticalPanel();
- fp.setStyleName("gerrit-AddSshKeyPanel");
- fp.add(new SmallHeading(Util.C.headingCreateGroup()));
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- projects.setRegisterKeys(true);
- }
-
- private class ProjectTable extends NavigationTable<Project> {
- ProjectTable() {
- setSavePointerId(Link.ADMIN_PROJECTS);
- keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.projectListPrev()));
- keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.projectListNext()));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.projectListOpen()));
- keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
- .projectListOpen()));
-
- table.setText(0, 1, Util.C.columnProjectName());
- table.setText(0, 2, Util.C.columnProjectDescription());
- table.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell != null && cell.getCellIndex() != 1
- && getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_DATA_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- }
-
- @Override
- protected Object getRowItemKey(final Project item) {
- return item.getId();
- }
-
- @Override
- protected void onOpenRow(final int row) {
- History.newItem(link(getRowItem(row)));
- }
-
- private String link(final Project item) {
- return Link.toProjectAdmin(item.getNameKey(), ProjectAdminScreen.INFO_TAB);
- }
-
- void display(final List<Project> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final Project k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, k);
- }
- }
-
- void populate(final int row, final Project k) {
- table.setWidget(row, 1, new Hyperlink(k.getName(), link(k)));
- table.setText(row, 2, k.getDescription());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_DATA_CELL);
- fmt.addStyleName(row, 1, "C_PROJECT");
- fmt.addStyleName(row, 2, S_DATA_CELL);
-
- setRowItem(row, k);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java b/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
deleted file mode 100644
index dc453a6bdc..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/ProjectRightsPanel.java
+++ /dev/null
@@ -1,437 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountGroupSuggestOracle;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.BlurEvent;
-import com.google.gwt.event.dom.client.BlurHandler;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.FocusEvent;
-import com.google.gwt.event.dom.client.FocusHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.SuggestBox;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-public class ProjectRightsPanel extends Composite {
- private Project.NameKey projectName;
-
- private RightsTable rights;
- private Button delRight;
- private Button addRight;
- private ListBox catBox;
- private ListBox rangeMinBox;
- private ListBox rangeMaxBox;
- private NpTextBox nameTxtBox;
- private SuggestBox nameTxt;
-
- public ProjectRightsPanel(final Project.NameKey toShow) {
- projectName = toShow;
-
- final FlowPanel body = new FlowPanel();
- initRights(body);
- initWidget(body);
- }
-
- @Override
- protected void onLoad() {
- enableForm(false);
- super.onLoad();
-
- Util.PROJECT_SVC.projectDetail(projectName,
- new GerritCallback<ProjectDetail>() {
- public void onSuccess(final ProjectDetail result) {
- enableForm(true);
- display(result);
- }
- });
- }
-
- private void enableForm(final boolean on) {
- delRight.setEnabled(on);
-
- final boolean canAdd = on && catBox.getItemCount() > 0;
- addRight.setEnabled(canAdd);
- nameTxtBox.setEnabled(canAdd);
- catBox.setEnabled(canAdd);
- rangeMinBox.setEnabled(canAdd);
- rangeMaxBox.setEnabled(canAdd);
- }
-
- private void initRights(final Panel body) {
- final FlowPanel addPanel = new FlowPanel();
- addPanel.setStyleName("gerrit-AddSshKeyPanel");
-
- final Grid addGrid = new Grid(4, 2);
-
- catBox = new ListBox();
- rangeMinBox = new ListBox();
- rangeMaxBox = new ListBox();
-
- catBox.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(final ChangeEvent event) {
- populateRangeBoxes();
- }
- });
- for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
- .getApprovalTypes()) {
- final ApprovalCategory c = at.getCategory();
- catBox.addItem(c.getName(), c.getId().get());
- }
- for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes()
- .getActionTypes()) {
- final ApprovalCategory c = at.getCategory();
- if (Gerrit.getConfig().getWildProject().equals(projectName)
- && ApprovalCategory.OWN.equals(c.getId())) {
- // Giving out control of the WILD_PROJECT to other groups beyond
- // Administrators is dangerous. Having control over WILD_PROJECT
- // is about the same as having Administrator access as users are
- // able to affect grants in all projects on the system.
- //
- continue;
- }
- catBox.addItem(c.getName(), c.getId().get());
- }
- if (catBox.getItemCount() > 0) {
- catBox.setSelectedIndex(0);
- populateRangeBoxes();
- }
-
- addGrid.setText(0, 0, Util.C.columnApprovalCategory() + ":");
- addGrid.setWidget(0, 1, catBox);
-
- nameTxtBox = new NpTextBox();
- nameTxt = new SuggestBox(new AccountGroupSuggestOracle(), nameTxtBox);
- nameTxtBox.setVisibleLength(50);
- nameTxtBox.setText(Util.C.defaultAccountGroupName());
- nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- nameTxtBox.addFocusHandler(new FocusHandler() {
- @Override
- public void onFocus(FocusEvent event) {
- if (Util.C.defaultAccountGroupName().equals(nameTxtBox.getText())) {
- nameTxtBox.setText("");
- nameTxtBox.removeStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- nameTxtBox.addBlurHandler(new BlurHandler() {
- @Override
- public void onBlur(BlurEvent event) {
- if ("".equals(nameTxtBox.getText())) {
- nameTxtBox.setText(Util.C.defaultAccountGroupName());
- nameTxtBox.addStyleName("gerrit-InputFieldTypeHint");
- }
- }
- });
- addGrid.setText(1, 0, Util.C.columnGroupName() + ":");
- addGrid.setWidget(1, 1, nameTxt);
-
- addGrid.setText(2, 0, Util.C.columnRightRange() + ":");
- addGrid.setWidget(2, 1, rangeMinBox);
-
- addGrid.setText(3, 0, "");
- addGrid.setWidget(3, 1, rangeMaxBox);
-
- addRight = new Button(Util.C.buttonAddProjectRight());
- addRight.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddNewRight();
- }
- });
- addPanel.add(addGrid);
- addPanel.add(addRight);
-
- rights = new RightsTable();
-
- delRight = new Button(Util.C.buttonDeleteGroupMembers());
- delRight.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- rights.deleteChecked();
- }
- });
-
- body.add(new SmallHeading(Util.C.headingAccessRights()));
- body.add(rights);
- body.add(delRight);
- body.add(addPanel);
- }
-
- void display(final ProjectDetail result) {
- rights.display(result.groups, result.rights);
- }
-
- private void doAddNewRight() {
- int idx = catBox.getSelectedIndex();
- final ApprovalType at;
- ApprovalCategoryValue min, max;
- if (idx < 0) {
- return;
- }
- at =
- Gerrit.getConfig().getApprovalTypes().getApprovalType(
- new ApprovalCategory.Id(catBox.getValue(idx)));
- if (at == null) {
- return;
- }
-
- idx = rangeMinBox.getSelectedIndex();
- if (idx < 0) {
- return;
- }
- min = at.getValue(Short.parseShort(rangeMinBox.getValue(idx)));
- if (min == null) {
- return;
- }
-
- idx = rangeMaxBox.getSelectedIndex();
- if (idx < 0) {
- return;
- }
- max = at.getValue(Short.parseShort(rangeMaxBox.getValue(idx)));
- if (max == null) {
- return;
- }
-
- final String groupName = nameTxt.getText();
- if ("".equals(groupName)
- || Util.C.defaultAccountGroupName().equals(groupName)) {
- return;
- }
-
- if (min.getValue() > max.getValue()) {
- // If the user selects it backwards in the web UI, help them out
- // by reversing the order to what we would expect.
- //
- final ApprovalCategoryValue newMin = max;
- final ApprovalCategoryValue newMax = min;
- min = newMin;
- max = newMax;
- }
-
- addRight.setEnabled(false);
- Util.PROJECT_SVC.addRight(projectName, at.getCategory().getId(), groupName,
- min.getValue(), max.getValue(), new GerritCallback<ProjectDetail>() {
- public void onSuccess(final ProjectDetail result) {
- addRight.setEnabled(true);
- nameTxt.setText("");
- display(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addRight.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void populateRangeBoxes() {
- final int idx = catBox.getSelectedIndex();
- final ApprovalType at;
- if (idx >= 0) {
- at =
- Gerrit.getConfig().getApprovalTypes().getApprovalType(
- new ApprovalCategory.Id(catBox.getValue(idx)));
- } else {
- at = null;
- }
-
- if (at != null && !at.getValues().isEmpty()) {
- int curIndex = 0, minIndex = -1, maxIndex = -1;
- rangeMinBox.clear();
- rangeMaxBox.clear();
- for (final ApprovalCategoryValue v : at.getValues()) {
- final String vStr = String.valueOf(v.getValue());
- String nStr = vStr + ": " + v.getName();
- if (v.getValue() > 0) {
- nStr = "+" + nStr;
- }
-
- rangeMinBox.addItem(nStr, vStr);
- rangeMaxBox.addItem(nStr, vStr);
-
- if (v.getValue() < 0) {
- minIndex = curIndex;
- }
- if (maxIndex < 0 && v.getValue() > 0) {
- maxIndex = curIndex;
- }
-
- curIndex++;
- }
- if (ApprovalCategory.READ.equals(at.getCategory().getId())) {
- // Special case; for READ the most logical range is just
- // +1 READ, so assume that as the default for both.
- minIndex = maxIndex;
- }
- rangeMinBox.setSelectedIndex(minIndex >= 0 ? minIndex : 0);
- rangeMaxBox.setSelectedIndex(maxIndex >= 0 ? maxIndex : curIndex - 1);
- } else {
- rangeMinBox.setEnabled(false);
- rangeMaxBox.setEnabled(false);
- }
- }
-
- private class RightsTable extends FancyFlexTable<ProjectRight> {
- RightsTable() {
- table.setWidth("");
- table.setText(0, 2, Util.C.columnApprovalCategory());
- table.setText(0, 3, Util.C.columnGroupName());
- table.setText(0, 4, Util.C.columnRightRange());
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, 1, S_ICON_HEADER);
- fmt.addStyleName(0, 2, S_DATA_HEADER);
- fmt.addStyleName(0, 3, S_DATA_HEADER);
- fmt.addStyleName(0, 4, S_DATA_HEADER);
- }
-
- void deleteChecked() {
- final HashSet<ProjectRight.Key> ids = new HashSet<ProjectRight.Key>();
- for (int row = 1; row < table.getRowCount(); row++) {
- final ProjectRight k = getRowItem(row);
- if (k != null && table.getWidget(row, 1) instanceof CheckBox
- && ((CheckBox) table.getWidget(row, 1)).getValue()) {
- ids.add(k.getKey());
- }
- }
- if (!ids.isEmpty()) {
- Util.PROJECT_SVC.deleteRight(projectName, ids,
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- for (int row = 1; row < table.getRowCount();) {
- final ProjectRight k = getRowItem(row);
- if (k != null && ids.contains(k.getKey())) {
- table.removeRow(row);
- } else {
- row++;
- }
- }
- }
- });
- }
- }
-
- void display(final Map<AccountGroup.Id, AccountGroup> groups,
- final List<ProjectRight> result) {
- while (1 < table.getRowCount())
- table.removeRow(table.getRowCount() - 1);
-
- for (final ProjectRight k : result) {
- final int row = table.getRowCount();
- table.insertRow(row);
- applyDataRowStyle(row);
- populate(row, groups, k);
- }
- }
-
- void populate(final int row,
- final Map<AccountGroup.Id, AccountGroup> groups, final ProjectRight k) {
- final GerritConfig config = Gerrit.getConfig();
- final ApprovalType ar =
- config.getApprovalTypes().getApprovalType(k.getApprovalCategoryId());
- final AccountGroup group = groups.get(k.getAccountGroupId());
-
- if (Gerrit.getConfig().getWildProject().equals(k.getProjectNameKey())
- && !Gerrit.getConfig().getWildProject().equals(projectName)) {
- table.setText(row, 1, "");
- } else {
- table.setWidget(row, 1, new CheckBox());
- }
-
- if (ar != null) {
- table.setText(row, 2, ar.getCategory().getName());
- } else {
- table.setText(row, 2, k.getApprovalCategoryId().get());
- }
-
- if (group != null) {
- table.setText(row, 3, group.getName());
- } else {
- table.setText(row, 3, Util.M.deletedGroup(k.getAccountGroupId().get()));
- }
-
- {
- final SafeHtmlBuilder m = new SafeHtmlBuilder();
- final ApprovalCategoryValue min, max;
- min = ar != null ? ar.getValue(k.getMinValue()) : null;
- max = ar != null ? ar.getValue(k.getMaxValue()) : null;
-
- formatValue(m, k.getMinValue(), min);
- if (k.getMinValue() != k.getMaxValue()) {
- m.br();
- formatValue(m, k.getMaxValue(), max);
- }
- SafeHtml.set(table, row, 4, m);
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(row, 1, S_ICON_CELL);
- fmt.addStyleName(row, 2, S_DATA_CELL);
- fmt.addStyleName(row, 3, S_DATA_CELL);
- fmt.addStyleName(row, 4, S_DATA_CELL);
- fmt.addStyleName(row, 4, "gerrit-ProjectAdmin-ApprovalCategoryRangeLine");
-
- setRowItem(row, k);
- }
-
- private void formatValue(final SafeHtmlBuilder m, final short v,
- final ApprovalCategoryValue e) {
- m.openSpan();
- m.setStyleName("gerrit-ProjectAdmin-ApprovalCategoryValue");
- if (v == 0) {
- m.append(' ');
- } else if (v > 0) {
- m.append('+');
- }
- m.append(v);
- m.closeSpan();
- if (e != null) {
- m.append(": ");
- m.append(e.getName());
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/admin/Util.java b/src/main/java/com/google/gerrit/client/admin/Util.java
deleted file mode 100644
index 7632e60f6f..0000000000
--- a/src/main/java/com/google/gerrit/client/admin/Util.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.admin;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final AdminConstants C = GWT.create(AdminConstants.class);
- public static final AdminMessages M = GWT.create(AdminMessages.class);
- public static final GroupAdminService GROUP_SVC;
- public static final ProjectAdminService PROJECT_SVC;
-
- static {
- GROUP_SVC = GWT.create(GroupAdminService.class);
- JsonUtil.bind(GROUP_SVC, "rpc/GroupAdminService");
-
- PROJECT_SVC = GWT.create(ProjectAdminService.class);
- JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService");
- }
-
- public static String toLongString(final Project.SubmitType type) {
- if (type == null) {
- return "";
- }
- switch (type) {
- case FAST_FORWARD_ONLY:
- return C.projectSubmitType_FAST_FORWARD_ONLY();
- case MERGE_IF_NECESSARY:
- return C.projectSubmitType_MERGE_IF_NECESSARY();
- case MERGE_ALWAYS:
- return C.projectSubmitType_MERGE_ALWAYS();
- case CHERRY_PICK:
- return C.projectSubmitType_CHERRY_PICK();
- default:
- return type.name();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/DiscoveryResult.java b/src/main/java/com/google/gerrit/client/auth/openid/DiscoveryResult.java
deleted file mode 100644
index b2cfd58378..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/openid/DiscoveryResult.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.openid;
-
-import java.util.Map;
-
-public final class DiscoveryResult {
- public boolean validProvider;
- public String providerUrl;
- public Map<String, String> providerArgs;
-
- protected DiscoveryResult() {
- }
-
- public DiscoveryResult(final boolean valid, final String redirect,
- final Map<String, String> args) {
- validProvider = valid;
- providerUrl = redirect;
- providerArgs = args;
- }
-
- public DiscoveryResult(final boolean fail) {
- this(false, null, null);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdService.java b/src/main/java/com/google/gerrit/client/auth/openid/OpenIdService.java
deleted file mode 100644
index 062651639e..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdService.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.openid;
-
-import com.google.gerrit.client.SignInDialog;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-public interface OpenIdService extends RemoteJsonService {
- @AllowCrossSiteRequest
- void discover(String openidIdentifier, SignInDialog.Mode mode,
- boolean remember, String returnToken,
- AsyncCallback<DiscoveryResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java b/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java
deleted file mode 100644
index a3a6222bd2..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSignInDialog.java
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.openid;
-
-import com.google.gerrit.client.SignInDialog;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.FormElement;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Cookies;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.AbstractImagePrototype;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.Hidden;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-import java.util.Map;
-
-public class OpenIdSignInDialog extends SignInDialog implements
- FormPanel.SubmitHandler {
- private final LoginIcons icons;
- private final FlowPanel panelWidget;
- private final FormPanel form;
- private final FlowPanel formBody;
- private final FormPanel redirectForm;
- private final FlowPanel redirectBody;
-
- private FlowPanel errorLine;
- private InlineLabel errorMsg;
-
- private Button login;
- private NpTextBox providerId;
- private CheckBox rememberId;
- private boolean discovering;
-
- public OpenIdSignInDialog(final SignInDialog.Mode requestedMode,
- final String initialErrorMsg) {
- super(requestedMode);
-
- icons = GWT.create(LoginIcons.class);
-
- formBody = new FlowPanel();
- formBody.setStyleName("gerrit-OpenID-loginform");
-
- form = new FormPanel();
- form.setMethod(FormPanel.METHOD_GET);
- form.addSubmitHandler(this);
- form.add(formBody);
-
- redirectBody = new FlowPanel();
- redirectBody.setVisible(false);
- redirectForm = new FormPanel();
- redirectForm.add(redirectBody);
-
- panelWidget = new FlowPanel();
- panelWidget.add(form);
- panelWidget.add(redirectForm);
- add(panelWidget);
-
- createHeaderLogo();
- createHeaderText();
- createErrorBox();
- createIdentBox();
-
- link(OpenIdUtil.URL_GOOGLE, OpenIdUtil.C.nameGoogle(), icons.iconGoogle());
- link(OpenIdUtil.URL_YAHOO, OpenIdUtil.C.nameYahoo(), icons.iconYahoo());
-
- if (initialErrorMsg != null) {
- showError(initialErrorMsg);
- }
- formBody.add(new HTML(OpenIdUtil.C.whatIsOpenIDHtml()));
- }
-
- @Override
- public void show() {
- super.show();
- providerId.selectAll();
- DeferredCommand.addCommand(new Command() {
- @Override
- public void execute() {
- providerId.setFocus(true);
- }
- });
- }
-
- private void createHeaderLogo() {
- final FlowPanel headerLogo = new FlowPanel();
- headerLogo.setStyleName("gerrit-OpenID-logobox");
- headerLogo.add(icons.openidLogo().createImage());
- formBody.add(headerLogo);
- }
-
- private void createHeaderText() {
- final FlowPanel headerText = new FlowPanel();
- final String me = Window.Location.getHostName();
- final SmallHeading headerLabel = new SmallHeading();
- switch (mode) {
- case LINK_IDENTIY:
- headerLabel.setText(OpenIdUtil.M.linkAt(me));
- break;
- case REGISTER:
- headerLabel.setText(OpenIdUtil.M.registerAt(me));
- break;
- case SIGN_IN:
- default:
- headerLabel.setText(OpenIdUtil.M.signInAt(me));
- break;
- }
- headerText.add(headerLabel);
- formBody.add(headerText);
- }
-
- private void createErrorBox() {
- errorLine = new FlowPanel();
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
- errorLine.setStyleName("gerrit-OpenID-errorline");
-
- errorMsg = new InlineLabel();
- errorLine.add(errorMsg);
- formBody.add(errorLine);
- }
-
- private void showError(final String msgText) {
- errorMsg.setText(msgText);
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "");
- }
-
- private void hideError() {
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
- }
-
- private void createIdentBox() {
- final FlowPanel group = new FlowPanel();
- group.setStyleName("gerrit-OpenID-loginline");
-
- final FlowPanel line1 = new FlowPanel();
- group.add(line1);
-
- providerId = new NpTextBox();
- providerId.setVisibleLength(60);
- providerId.setStyleName("gerrit-OpenID-openid_identifier");
- providerId.setTabIndex(0);
- providerId.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getCharCode() == KeyCodes.KEY_ENTER) {
- event.preventDefault();
- form.submit();
- }
- }
- });
- line1.add(providerId);
-
- login = new Button();
- switch (mode) {
- case LINK_IDENTIY:
- login.setText(OpenIdUtil.C.buttonLinkId());
- break;
- case REGISTER:
- login.setText(OpenIdUtil.C.buttonRegister());
- break;
- case SIGN_IN:
- default:
- login.setText(OpenIdUtil.C.buttonSignIn());
- break;
- }
- login.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- form.submit();
- }
- });
- login.setTabIndex(2);
- line1.add(login);
-
- if (mode == SignInDialog.Mode.SIGN_IN) {
- rememberId = new CheckBox(OpenIdUtil.C.rememberMe());
- rememberId.setTabIndex(1);
- group.add(rememberId);
-
- final String last = Cookies.getCookie(OpenIdUtil.LASTID_COOKIE);
- if (last != null && !"".equals(last)) {
- providerId.setText(last);
- rememberId.setValue(true);
- }
- }
-
- formBody.add(group);
- }
-
- private void link(final String identUrl, final String who,
- final AbstractImagePrototype icon) {
- final ClickHandler i = new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- if (!discovering) {
- providerId.setText(identUrl);
- form.submit();
- }
- }
- };
-
- final FlowPanel line = new FlowPanel();
- line.addStyleName("gerrit-OpenID-directlink");
-
- final Image img = icon.createImage();
- img.addClickHandler(i);
- line.add(img);
-
- final InlineLabel lbl = new InlineLabel();
- switch (mode) {
- case LINK_IDENTIY:
- lbl.setText(OpenIdUtil.M.linkWith(who));
- break;
- case REGISTER:
- lbl.setText(OpenIdUtil.M.registerWith(who));
- break;
- case SIGN_IN:
- default:
- lbl.setText(OpenIdUtil.M.signInWith(who));
- break;
- }
- lbl.addClickHandler(i);
- line.add(lbl);
-
- formBody.add(line);
- }
-
- private void enable(final boolean on) {
- providerId.setEnabled(on);
- login.setEnabled(on);
- }
-
- private void onDiscovery(final DiscoveryResult result) {
- discovering = false;
-
- if (result.validProvider) {
- redirectForm.setMethod(FormPanel.METHOD_POST);
- redirectForm.setAction(result.providerUrl);
- redirectBody.clear();
- for (final Map.Entry<String, String> e : result.providerArgs.entrySet()) {
- redirectBody.add(new Hidden(e.getKey(), e.getValue()));
- }
-
- // The provider won't support operation inside an IFRAME, so we
- // replace our entire application. No fancy waits are needed,
- // the browser won't update anything until its started to load
- // the provider's page.
- //
- FormElement.as(redirectForm.getElement()).setTarget("_top");
- redirectForm.submit();
-
- } else {
- // We failed discovery. We have to use a deferred command here
- // as we are being called from within an invisible IFRAME. Jump
- // back to the main event loop in the parent window.
- //
- onDiscoveryFailure();
- }
- }
-
- private void onDiscoveryFailure() {
- showError(OpenIdUtil.C.notSupported());
- enable(true);
- providerId.selectAll();
- providerId.setFocus(true);
- }
-
- @Override
- public void onSubmit(final SubmitEvent event) {
- event.cancel();
-
- final String openidIdentifier = providerId.getText();
- if (openidIdentifier == null || openidIdentifier.equals("")) {
- enable(true);
- return;
- }
-
- discovering = true;
- enable(false);
- hideError();
-
- final boolean remember = rememberId != null && rememberId.getValue();
- final String token = History.getToken();
- OpenIdUtil.SVC.discover(openidIdentifier, mode, remember, token,
- new GerritCallback<DiscoveryResult>() {
- public void onSuccess(final DiscoveryResult result) {
- onDiscovery(result);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- super.onFailure(caught);
- onDiscoveryFailure();
- }
- });
- }
-
- public void onSubmitComplete(final FormSubmitCompleteEvent event) {
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java b/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
deleted file mode 100644
index d9a7c40a14..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/openid/OpenIdUtil.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.openid;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class OpenIdUtil {
- public static final LoginConstants C;
- public static final LoginMessages M;
- public static final OpenIdService SVC;
-
- public static final String OPENID_IDENTIFIER = "openid_identifier";
- public static final String LASTID_COOKIE = "gerrit.last_openid";
-
- public static final String URL_YAHOO = "https://me.yahoo.com";
- public static final String URL_GOOGLE =
- "https://www.google.com/accounts/o8/id";
-
- static {
- if (GWT.isClient()) {
- C = GWT.create(LoginConstants.class);
- M = GWT.create(LoginMessages.class);
- SVC = GWT.create(OpenIdService.class);
- JsonUtil.bind(SVC, "rpc/OpenIdService");
- } else {
- C = null;
- M = null;
- SVC = null;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/LoginResult.java b/src/main/java/com/google/gerrit/client/auth/userpass/LoginResult.java
deleted file mode 100644
index d960a3d1fe..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/userpass/LoginResult.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.userpass;
-
-public class LoginResult {
- public boolean success;
- public boolean isNew;
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/UserPassAuthService.java b/src/main/java/com/google/gerrit/client/auth/userpass/UserPassAuthService.java
deleted file mode 100644
index 3ed6968a20..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/userpass/UserPassAuthService.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.userpass;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-public interface UserPassAuthService extends RemoteJsonService {
- @AllowCrossSiteRequest
- void authenticate(String username, String password,
- AsyncCallback<LoginResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java b/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java
deleted file mode 100644
index 5c0fbc1429..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/userpass/UserPassSignInDialog.java
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.userpass;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.SignInDialog;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.Window.Location;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.PasswordTextBox;
-import com.google.gwt.user.client.ui.TextBox;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.NpTextBox;
-
-public class UserPassSignInDialog extends SignInDialog {
- private final FlowPanel formBody;
-
- private FlowPanel errorLine;
- private InlineLabel errorMsg;
-
- private Button login;
- private Button close;
- private TextBox username;
- private TextBox password;
-
- public UserPassSignInDialog(final String initialErrorMsg) {
- super(Mode.SIGN_IN);
-
- formBody = new FlowPanel();
- formBody.setStyleName("gerrit-OpenID-loginform");
- add(formBody);
-
- createHeaderText();
- createErrorBox();
- createUsernameBox();
- if (initialErrorMsg != null) {
- showError(initialErrorMsg);
- }
- }
-
- @Override
- public void show() {
- super.show();
- DeferredCommand.addCommand(new Command() {
- @Override
- public void execute() {
- username.setFocus(true);
- }
- });
- }
-
- private void createHeaderText() {
- final FlowPanel headerText = new FlowPanel();
- final SmallHeading headerLabel = new SmallHeading();
- headerLabel.setText(Util.M.signInAt(Location.getHostName()));
- headerText.add(headerLabel);
- formBody.add(headerText);
- }
-
- private void createErrorBox() {
- errorLine = new FlowPanel();
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
- errorLine.setStyleName("gerrit-OpenID-errorline");
-
- errorMsg = new InlineLabel();
- errorLine.add(errorMsg);
- formBody.add(errorLine);
- }
-
- private void showError(final String msgText) {
- errorMsg.setText(msgText);
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "");
- }
-
- private void hideError() {
- DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden");
- }
-
- private void createUsernameBox() {
- username = new NpTextBox();
- username.setVisibleLength(25);
- username.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getCharCode() == KeyCodes.KEY_ENTER) {
- event.preventDefault();
- password.selectAll();
- password.setFocus(true);
- }
- }
- });
-
- password = new PasswordTextBox();
- password.setVisibleLength(25);
- password.addKeyPressHandler(GlobalKey.STOP_PROPAGATION);
- password.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getCharCode() == KeyCodes.KEY_ENTER) {
- event.preventDefault();
- onLogin();
- }
- }
- });
-
- final FlowPanel buttons = new FlowPanel();
- buttons.setStyleName("gerrit-ErrorDialog-Buttons");
-
- login = new Button();
- login.setText(Util.C.buttonSignIn());
- login.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- onLogin();
- }
- });
- buttons.add(login);
-
- close = new Button();
- close.setText(Gerrit.C.errorDialogClose());
- close.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- }
- });
- buttons.add(close);
-
- final Grid formGrid = new Grid(3, 2);
- formGrid.setText(0, 0, Util.C.username());
- formGrid.setText(1, 0, Util.C.password());
- formGrid.setWidget(0, 1, username);
- formGrid.setWidget(1, 1, password);
- formGrid.setWidget(2, 1, buttons);
- formBody.add(formGrid);
-
- username.setTabIndex(1);
- password.setTabIndex(2);
- login.setTabIndex(3);
- close.setTabIndex(4);
- }
-
- private void enable(final boolean on) {
- username.setEnabled(on);
- password.setEnabled(on);
- login.setEnabled(on);
- }
-
- private void onLogin() {
- hideError();
-
- final String user = username.getText();
- if (user == null || user.equals("")) {
- showError(Util.C.usernameRequired());
- username.setFocus(true);
- return;
- }
-
- final String pass = password.getText();
- if (pass == null || pass.equals("")) {
- showError(Util.C.passwordRequired());
- password.setFocus(true);
- return;
- }
-
- enable(false);
- Util.SVC.authenticate(user, pass, new GerritCallback<LoginResult>() {
- public void onSuccess(final LoginResult result) {
- if (result.success) {
- String token = History.getToken();
- if (result.isNew && !token.startsWith(Link.REGISTER + ",")) {
- token = Link.REGISTER + "," + token;
- }
-
- // Unfortunately we no longer support updating the web UI when the
- // user signs in. Instead we must force a reload of the page, but
- // that isn't easy because we might need to change the anchor. So
- // we bounce through a little redirection servlet on the server.
- //
- Location.replace(Location.getPath() + "login/" + token);
- } else {
- showError(Util.C.invalidLogin());
- enable(true);
- password.selectAll();
- DeferredCommand.addCommand(new Command() {
- @Override
- public void execute() {
- password.setFocus(true);
- }
- });
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- super.onFailure(caught);
- enable(false);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/auth/userpass/Util.java b/src/main/java/com/google/gerrit/client/auth/userpass/Util.java
deleted file mode 100644
index 5e499794c5..0000000000
--- a/src/main/java/com/google/gerrit/client/auth/userpass/Util.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.auth.userpass;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final LoginConstants C = GWT.create(LoginConstants.class);
- public static final LoginMessages M = GWT.create(LoginMessages.class);
- public static final UserPassAuthService SVC;
-
- static {
- SVC = GWT.create(UserPassAuthService.class);
- JsonUtil.bind(SVC, "rpc/UserPassAuthService");
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java b/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java
deleted file mode 100644
index a7082f1804..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/AbandonChangeDialog.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-public class AbandonChangeDialog extends AutoCenterDialogBox {
- private final FlowPanel panel;
- private final NpTextArea message;
- private final Button sendButton;
- private final Button cancelButton;
- private final PatchSet.Id psid;
-
- public AbandonChangeDialog(final PatchSet.Id psi,
- final AsyncCallback<ChangeDetail> callback) {
- super(/* auto hide */true, /* modal */true);
-
- psid = psi;
- addStyleName("gerrit-AbandonChangeDialog");
- setText(Util.C.abandonChangeTitle());
-
- panel = new FlowPanel();
- add(panel);
-
- panel.add(new SmallHeading(Util.C.headingAbandonMessage()));
-
- final FlowPanel mwrap = new FlowPanel();
- mwrap.setStyleName("gerrit-AbandonMessage");
- panel.add(mwrap);
-
- message = new NpTextArea();
- message.setCharacterWidth(60);
- message.setVisibleLines(10);
- DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
- mwrap.add(message);
-
- final FlowPanel buttonPanel = new FlowPanel();
- buttonPanel.setStyleName("gerrit-CommentEditor-Buttons");
- panel.add(buttonPanel);
-
- sendButton = new Button(Util.C.buttonAbandonChangeSend());
- sendButton.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- sendButton.setEnabled(false);
- Util.MANAGE_SVC.abandonChange(psid, message.getText().trim(),
- new GerritCallback<ChangeDetail>() {
- public void onSuccess(ChangeDetail result) {
- if (callback != null) {
- callback.onSuccess(result);
- }
- hide();
- }
-
- @Override
- public void onFailure(Throwable caught) {
- sendButton.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
- });
- buttonPanel.add(sendButton);
-
- cancelButton = new Button(Util.C.buttonAbandonChangeCancel());
- cancelButton.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- if (callback != null) {
- callback.onFailure(null);
- }
- hide();
- }
- });
- buttonPanel.add(cancelButton);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- message.setFocus(true);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java b/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
deleted file mode 100644
index e7298a54b8..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/AccountDashboardScreen.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.ChangeTable.ApprovalViewType;
-import com.google.gerrit.client.data.AccountDashboardInfo;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.Screen;
-
-
-public class AccountDashboardScreen extends Screen {
- private final Account.Id ownerId;
- private ChangeTable table;
- private ChangeTable.Section byOwner;
- private ChangeTable.Section forReview;
- private ChangeTable.Section closed;
-
- public AccountDashboardScreen(final Account.Id id) {
- ownerId = id;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- table = new ChangeTable(true);
- table.addStyleName("gerrit-AccountDashboard");
- byOwner = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
- forReview = new ChangeTable.Section("", ApprovalViewType.USER, ownerId);
- closed = new ChangeTable.Section("", ApprovalViewType.STRONGEST, null);
-
- table.addSection(byOwner);
- table.addSection(forReview);
- table.addSection(closed);
- add(table);
- table.setSavePointerId(Link.toAccountDashboard(ownerId));
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.LIST_SVC.forAccount(ownerId,
- new ScreenLoadCallback<AccountDashboardInfo>(this) {
- @Override
- protected void preDisplay(final AccountDashboardInfo r) {
- display(r);
- }
- });
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- private void display(final AccountDashboardInfo r) {
- table.setAccountInfoCache(r.getAccounts());
-
- final AccountInfo o = r.getAccounts().get(r.getOwner());
- final String name = FormatUtil.name(o);
- setWindowTitle(name);
- setPageTitle(Util.M.accountDashboardTitle(name));
- byOwner.setTitleText(Util.M.changesStartedBy(name));
- forReview.setTitleText(Util.M.changesReviewableBy(name));
- closed.setTitleText(Util.C.changesRecentlyClosed());
-
- byOwner.display(r.getByOwner());
- forReview.display(r.getForReview());
- closed.display(r.getClosed());
- table.finishDisplay();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java b/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java
deleted file mode 100644
index a157f6d002..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/AllAbandonedChangesScreen.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Change;
-
-
-public class AllAbandonedChangesScreen extends AllSingleListScreen {
- public AllAbandonedChangesScreen(final String positionToken) {
- super("all,abandoned", positionToken);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Gerrit.C.menuAllAbandoned());
- setPageTitle(Util.C.allAbandonedChanges());
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.allClosedPrev(Change.Status.ABANDONED, pos, pageSize,
- loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.allClosedNext(Change.Status.ABANDONED, pos, pageSize,
- loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java b/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java
deleted file mode 100644
index ae27542422..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/AllMergedChangesScreen.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.Change;
-
-
-public class AllMergedChangesScreen extends AllSingleListScreen {
- public AllMergedChangesScreen(final String positionToken) {
- super("all,merged", positionToken);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Gerrit.C.menuAllMerged());
- setPageTitle(Util.C.allMergedChanges());
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.allClosedPrev(Change.Status.MERGED, pos, pageSize,
- loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.allClosedNext(Change.Status.MERGED, pos, pageSize,
- loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java b/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java
deleted file mode 100644
index 23a3b04243..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/AllSingleListScreen.java
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.data.SingleListChangeInfo;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwt.user.client.ui.Hyperlink;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-
-import java.util.List;
-
-
-public abstract class AllSingleListScreen extends Screen {
- protected static final String MIN_SORTKEY = "";
- protected static final String MAX_SORTKEY = "z";
-
- protected final int pageSize;
- private ChangeTable table;
- private ChangeTable.Section section;
- protected Hyperlink prev;
- protected Hyperlink next;
- protected List<ChangeInfo> changes;
-
- protected final String anchorPrefix;
- protected boolean useLoadPrev;
- protected String pos;
-
- protected AllSingleListScreen(final String anchorToken,
- final String positionToken) {
- anchorPrefix = anchorToken;
- useLoadPrev = positionToken.startsWith("p,");
- pos = positionToken.substring(2);
-
- if (Gerrit.isSignedIn()) {
- final AccountGeneralPreferences p =
- Gerrit.getUserAccount().getGeneralPreferences();
- final short m = p.getMaximumPageSize();
- pageSize = 0 < m ? m : AccountGeneralPreferences.DEFAULT_PAGESIZE;
- } else {
- pageSize = AccountGeneralPreferences.DEFAULT_PAGESIZE;
- }
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- prev = new Hyperlink(Util.C.pagedChangeListPrev(), true, "");
- prev.setVisible(false);
-
- next = new Hyperlink(Util.C.pagedChangeListNext(), true, "");
- next.setVisible(false);
-
- table = new ChangeTable() {
- {
- keysNavigation.add(new DoLinkCommand(0, 'p', Util.C
- .changeTablePagePrev(), prev));
- keysNavigation.add(new DoLinkCommand(0, 'n', Util.C
- .changeTablePageNext(), next));
- }
- };
- section = new ChangeTable.Section();
-
- table.addSection(section);
- table.setSavePointerId(anchorPrefix);
- add(table);
-
- final HorizontalPanel buttons = new HorizontalPanel();
- buttons.setStyleName("gerrit-ChangeTable-PrevNextLinks");
- buttons.add(prev);
- buttons.add(next);
- add(buttons);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- if (useLoadPrev) {
- loadPrev();
- } else {
- loadNext();
- }
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- protected abstract void loadPrev();
-
- protected abstract void loadNext();
-
- protected AsyncCallback<SingleListChangeInfo> loadCallback() {
- return new ScreenLoadCallback<SingleListChangeInfo>(this) {
- @Override
- protected void preDisplay(final SingleListChangeInfo result) {
- display(result);
- }
- };
- }
-
- protected void display(final SingleListChangeInfo result) {
- changes = result.getChanges();
-
- if (!changes.isEmpty()) {
- final ChangeInfo f = changes.get(0);
- final ChangeInfo l = changes.get(changes.size() - 1);
-
- prev.setTargetHistoryToken(anchorPrefix + ",p," + f.getSortKey());
- next.setTargetHistoryToken(anchorPrefix + ",n," + l.getSortKey());
-
- if (useLoadPrev) {
- prev.setVisible(!result.isAtEnd());
- next.setVisible(!MIN_SORTKEY.equals(pos));
- } else {
- prev.setVisible(!MAX_SORTKEY.equals(pos));
- next.setVisible(!result.isAtEnd());
- }
- }
-
- table.setAccountInfoCache(result.getAccounts());
- section.display(result.getChanges());
- table.finishDisplay();
- }
-
- private static final class DoLinkCommand extends KeyCommand {
- private final Hyperlink link;
-
- private DoLinkCommand(int mask, char key, String help, Hyperlink l) {
- super(mask, key, help);
- link = l;
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (link.isVisible()) {
- History.newItem(link.getTargetHistoryToken());
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java b/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
deleted file mode 100644
index 66221940a6..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ApprovalTable.java
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.data.ApprovalDetail;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.patches.AddReviewerResult;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountDashboardLink;
-import com.google.gerrit.client.ui.AddMemberBox;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Displays a table of {@link ApprovalDetail} objects for a change record. */
-public class ApprovalTable extends Composite {
- private final List<ApprovalType> types;
- private final Grid table;
- private final Widget missing;
- private final Panel addReviewer;
- private final AddMemberBox addMemberBox;
- private Change.Id changeId;
- private boolean changeIsOpen;
- private AccountInfoCache accountCache = AccountInfoCache.empty();
-
- public ApprovalTable() {
- types = Gerrit.getConfig().getApprovalTypes().getApprovalTypes();
- table = new Grid(1, 3 + types.size());
- table.addStyleName("gerrit-InfoTable");
- displayHeader();
-
- missing = new Widget() {
- {
- setElement(DOM.createElement("ul"));
- }
- };
- missing.setStyleName("gerrit-MissingApprovalList");
-
- addReviewer = new FlowPanel();
- addReviewer.setStyleName("gerrit-AddReviewer");
- addMemberBox = new AddMemberBox();
- addMemberBox.setAddButtonText(Util.C.approvalTableAddReviewer());
- addMemberBox.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- doAddReviewer();
- }
- });
- addReviewer.add(addMemberBox);
- addReviewer.setVisible(false);
-
- final FlowPanel fp = new FlowPanel();
- fp.add(table);
- fp.add(missing);
- fp.add(addReviewer);
- initWidget(fp);
-
- setStyleName("gerrit-ApprovalTable");
- }
-
- private void displayHeader() {
- int col = 0;
- header(col++, Util.C.approvalTableReviewer());
- header(col++, "");
-
- for (final ApprovalType t : types) {
- header(col++, t.getCategory().getName());
- }
- applyEdgeStyles(0);
- }
-
- private void header(final int col, final String title) {
- table.setText(0, col, title);
- table.getCellFormatter().addStyleName(0, col, "header");
- }
-
- private void applyEdgeStyles(final int row) {
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, 0, "leftmost");
- fmt.addStyleName(row, 0, "reviewer");
- fmt.addStyleName(row, 1, "approvalrole");
- fmt.addStyleName(row, 1 + types.size(), "rightmost");
- fmt.addStyleName(row, 2 + types.size(), "approvalhint");
- }
-
- private void applyScoreStyles(final int row) {
- final CellFormatter fmt = table.getCellFormatter();
- for (int col = 0; col < types.size(); col++) {
- fmt.addStyleName(row, 2 + col, "approvalscore");
- }
- }
-
- public void setAccountInfoCache(final AccountInfoCache aic) {
- assert aic != null;
- accountCache = aic;
- }
-
- private AccountDashboardLink link(final Account.Id id) {
- return AccountDashboardLink.link(accountCache, id);
- }
-
- public void display(final Change change, final Set<ApprovalCategory.Id> need,
- final List<ApprovalDetail> rows) {
- changeId = change.getId();
-
- final int oldcnt = table.getRowCount();
- table.resizeRows(1 + rows.size());
- if (oldcnt < 1 + rows.size()) {
- for (int row = oldcnt; row < 1 + rows.size(); row++) {
- applyEdgeStyles(row);
- applyScoreStyles(row);
- }
- }
-
- if (rows.isEmpty()) {
- table.setVisible(false);
- } else {
- table.setVisible(true);
- for (int i = 0; i < rows.size(); i++) {
- displayRow(i + 1, rows.get(i));
- }
- }
-
- final Element missingList = missing.getElement();
- while (DOM.getChildCount(missingList) > 0) {
- DOM.removeChild(missingList, DOM.getChild(missingList, 0));
- }
-
- missing.setVisible(false);
- if (need != null) {
- for (final ApprovalType at : types) {
- if (need.contains(at.getCategory().getId())) {
- final Element li = DOM.createElement("li");
- li.setClassName("gerrit-MissingApproval");
- DOM.setInnerText(li, Util.M.needApproval(at.getCategory().getName(),
- at.getMax().formatValue(), at.getMax().getName()));
- DOM.appendChild(missingList, li);
- missing.setVisible(true);
- }
- }
- }
-
- changeIsOpen = change.getStatus().isOpen();
- addReviewer.setVisible(Gerrit.isSignedIn() && changeIsOpen);
- }
-
- private void doAddReviewer() {
- final String nameEmail = addMemberBox.getText();
- if (nameEmail.length() == 0) {
- return;
- }
-
- addMemberBox.setEnabled(false);
- final List<String> reviewers = new ArrayList<String>();
- reviewers.add(nameEmail);
-
- PatchUtil.DETAIL_SVC.addReviewers(changeId, reviewers,
- new GerritCallback<AddReviewerResult>() {
- public void onSuccess(final AddReviewerResult result) {
- addMemberBox.setEnabled(true);
- addMemberBox.setText("");
-
- if (!result.getErrors().isEmpty()) {
- final SafeHtmlBuilder r = new SafeHtmlBuilder();
- for (final AddReviewerResult.Error e : result.getErrors()) {
- switch (e.getType()) {
- case ACCOUNT_NOT_FOUND:
- r.append(Util.M.accountNotFound(e.getName()));
- break;
-
- case CHANGE_NOT_VISIBLE:
- r.append(Util.M.changeNotVisibleTo(e.getName()));
- break;
-
- default:
- r.append(e.getName());
- r.append(" - ");
- r.append(e.getType());
- r.br();
- break;
- }
- }
- new ErrorDialog(r).center();
- }
-
- final ChangeDetail r = result.getChange();
- if (r != null) {
- setAccountInfoCache(r.getAccounts());
- display(r.getChange(), r.getMissingApprovals(), r.getApprovals());
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- addMemberBox.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void displayRow(final int row, final ApprovalDetail ad) {
- final CellFormatter fmt = table.getCellFormatter();
- final Map<ApprovalCategory.Id, PatchSetApproval> am = ad.getApprovalMap();
- final StringBuilder hint = new StringBuilder();
- int col = 0;
- table.setWidget(row, col++, link(ad.getAccount()));
- table.clearCell(row, col++); // TODO populate the account role
-
- for (final ApprovalType type : types) {
- final PatchSetApproval ca = am.get(type.getCategory().getId());
- if (ca == null || ca.getValue() == 0) {
- table.clearCell(row, col);
- col++;
- continue;
- }
-
- final ApprovalCategoryValue acv = type.getValue(ca);
- if (acv != null) {
- if (hint.length() > 0) {
- hint.append("; ");
- }
- hint.append(acv.getName());
- }
-
- if (type.isMaxNegative(ca)) {
- table.setWidget(row, col, Gerrit.ICONS.redNot().createImage());
-
- } else if (type.isMaxPositive(ca)) {
- table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
-
- } else {
- String vstr = String.valueOf(ca.getValue());
- if (ca.getValue() > 0) {
- vstr = "+" + vstr;
- fmt.removeStyleName(row, col, "negscore");
- fmt.addStyleName(row, col, "posscore");
- } else {
- fmt.addStyleName(row, col, "negscore");
- fmt.removeStyleName(row, col, "posscore");
- }
- table.setText(row, col, vstr);
- }
-
- col++;
- }
-
- table.setText(row, col++, hint.toString());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java b/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java
deleted file mode 100644
index c72beccea8..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ByProjectAbandonedChangesScreen.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-
-
-public class ByProjectAbandonedChangesScreen extends AllSingleListScreen {
- private final Project.NameKey projectKey;
-
- public ByProjectAbandonedChangesScreen(final Project.NameKey proj,
- final String positionToken) {
- super("project,abandoned," + proj.toString(), positionToken);
- projectKey = proj;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.M.changesAbandonedInProject(projectKey.get()));
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.byProjectClosedPrev(projectKey, Change.Status.ABANDONED, pos,
- pageSize, loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.byProjectClosedNext(projectKey, Change.Status.ABANDONED, pos,
- pageSize, loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java b/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java
deleted file mode 100644
index db8cb379dc..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ByProjectMergedChangesScreen.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-
-
-public class ByProjectMergedChangesScreen extends AllSingleListScreen {
- private final Project.NameKey projectKey;
-
- public ByProjectMergedChangesScreen(final Project.NameKey proj,
- final String positionToken) {
- super("project,merged," + proj.toString(), positionToken);
- projectKey = proj;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.M.changesMergedInProject(projectKey.get()));
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.byProjectClosedPrev(projectKey, Change.Status.MERGED, pos,
- pageSize, loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.byProjectClosedNext(projectKey, Change.Status.MERGED, pos,
- pageSize, loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java b/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java
deleted file mode 100644
index b8679bac91..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ByProjectOpenChangesScreen.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.reviewdb.Project;
-
-
-public class ByProjectOpenChangesScreen extends AllSingleListScreen {
- private final Project.NameKey projectKey;
-
- public ByProjectOpenChangesScreen(final Project.NameKey proj,
- final String positionToken) {
- super("project,open," + proj.toString(), positionToken);
- projectKey = proj;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setPageTitle(Util.M.changesOpenInProject(projectKey.get()));
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.byProjectOpenPrev(projectKey, pos, pageSize, loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.byProjectOpenNext(projectKey, pos, pageSize, loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java b/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
deleted file mode 100644
index 6bc3fb7890..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeDescriptionBlock.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HorizontalPanel;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public class ChangeDescriptionBlock extends Composite {
- private final ChangeInfoBlock infoBlock;
- private final HTML description;
-
- public ChangeDescriptionBlock() {
- infoBlock = new ChangeInfoBlock();
- description = new HTML();
- description.setStyleName("gerrit-ChangeScreen-Description");
-
- final HorizontalPanel hp = new HorizontalPanel();
- hp.add(infoBlock);
- hp.add(description);
- initWidget(hp);
- }
-
- public void display(final Change chg, final PatchSetInfo info,
- final AccountInfoCache acc) {
- infoBlock.display(chg, acc);
-
- SafeHtml msg = new SafeHtmlBuilder().append(info.getMessage());
- msg = msg.linkify();
- msg = msg.replaceAll(Gerrit.getConfig().getCommentLinks());
- msg = new SafeHtmlBuilder().openElement("p").append(msg).closeElement("p");
- msg = msg.replaceAll("\n\n", "</p><p>");
- msg = msg.replaceAll("\n", "<br />");
- SafeHtml.set(description, msg);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeDetailService.java b/src/main/java/com/google/gerrit/client/changes/ChangeDetailService.java
deleted file mode 100644
index af2d63dfe5..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeDetailService.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.data.PatchSetDetail;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-public interface ChangeDetailService extends RemoteJsonService {
- void changeDetail(Change.Id id, AsyncCallback<ChangeDetail> callback);
-
- void patchSetDetail(PatchSet.Id key, AsyncCallback<PatchSetDetail> callback);
-
- @SignInRequired
- void patchSetPublishDetail(PatchSet.Id key,
- AsyncCallback<PatchSetPublishDetail> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java b/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
deleted file mode 100644
index a1d08cf85b..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeInfoBlock.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import static com.google.gerrit.client.FormatUtil.mediumFormat;
-
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.ui.AccountDashboardLink;
-import com.google.gerrit.client.ui.ChangeLink;
-import com.google.gerrit.client.ui.ProjectLink;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-public class ChangeInfoBlock extends Composite {
- private static final int R_CHANGE_ID = 0;
- private static final int R_OWNER = 1;
- private static final int R_PROJECT = 2;
- private static final int R_BRANCH = 3;
- private static final int R_UPLOADED = 4;
- private static final int R_UPDATED = 5;
- private static final int R_STATUS = 6;
- private static final int R_PERMALINK = 7;
- private static final int R_CNT = 8;
-
- private final Grid table;
-
- public ChangeInfoBlock() {
- table = new Grid(R_CNT, 2);
- table.setStyleName("gerrit-InfoBlock");
- table.addStyleName("gerrit-ChangeInfoBlock");
-
- initRow(R_CHANGE_ID, "Change-Id: ");
- initRow(R_OWNER, Util.C.changeInfoBlockOwner());
- initRow(R_PROJECT, Util.C.changeInfoBlockProject());
- initRow(R_BRANCH, Util.C.changeInfoBlockBranch());
- initRow(R_UPLOADED, Util.C.changeInfoBlockUploaded());
- initRow(R_UPDATED, Util.C.changeInfoBlockUpdated());
- initRow(R_STATUS, Util.C.changeInfoBlockStatus());
-
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(0, 0, "topmost");
- fmt.addStyleName(0, 1, "topmost");
- fmt.addStyleName(R_CHANGE_ID, 1, "changeid");
- fmt.addStyleName(R_CNT - 2, 0, "bottomheader");
- fmt.addStyleName(R_PERMALINK, 0, "permalink");
- fmt.addStyleName(R_PERMALINK, 1, "permalink");
-
- initWidget(table);
- }
-
- private void initRow(final int row, final String name) {
- table.setText(row, 0, name);
- table.getCellFormatter().addStyleName(row, 0, "header");
- }
-
- public void display(final Change chg, final AccountInfoCache acc) {
- final Branch.NameKey dst = chg.getDest();
- table.setText(R_CHANGE_ID, 1, chg.getKey().get());
- table.setWidget(R_OWNER, 1, AccountDashboardLink.link(acc, chg.getOwner()));
- table.setWidget(R_PROJECT, 1, new ProjectLink(chg.getProject(), chg.getStatus()));
- table.setText(R_BRANCH, 1, dst.getShortName());
- table.setText(R_UPLOADED, 1, mediumFormat(chg.getCreatedOn()));
- table.setText(R_UPDATED, 1, mediumFormat(chg.getLastUpdatedOn()));
- table.setText(R_STATUS, 1, Util.toLongString(chg.getStatus()));
-
- if (chg.getStatus().isClosed()) {
- table.getCellFormatter().addStyleName(R_STATUS, 1, "closedstate");
- } else {
- table.getCellFormatter().removeStyleName(R_STATUS, 1, "closedstate");
- }
-
- final FlowPanel fp = new FlowPanel();
- fp.add(new ChangeLink(Util.C.changePermalink(), chg.getId()));
- fp.add(new CopyableLabel(ChangeLink.permalink(chg.getId()), false));
- table.setWidget(R_PERMALINK, 1, fp);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeListService.java b/src/main/java/com/google/gerrit/client/changes/ChangeListService.java
deleted file mode 100644
index e5f2668e5e..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeListService.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.AccountDashboardInfo;
-import com.google.gerrit.client.data.SingleListChangeInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.Set;
-
-public interface ChangeListService extends RemoteJsonService {
- /** Get all open changes more recent than pos, fetching at most limit rows. */
- void allOpenPrev(String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all open changes older than pos, fetching at most limit rows. */
- void allOpenNext(String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all open changes more recent than pos, fetching at most limit rows. */
- void byProjectOpenPrev(Project.NameKey project, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all open changes older than pos, fetching at most limit rows. */
- void byProjectOpenNext(Project.NameKey project, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /**
- * Get all closed changes with same status, more recent than pos, fetching at
- * most limit rows.
- */
- void byProjectClosedPrev(Project.NameKey project, Change.Status status,
- String pos, int limit, AsyncCallback<SingleListChangeInfo> callback);
-
- /**
- * Get all closed changes with same status, older than pos, fetching at most
- * limit rows.
- */
- void byProjectClosedNext(Project.NameKey project, Change.Status status,
- String pos, int limit, AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all closed changes more recent than pos, fetching at most limit rows. */
- void allClosedPrev(Change.Status status, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all closed changes older than pos, fetching at most limit rows. */
- void allClosedNext(Change.Status status, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all changes which match an arbitrary query string. */
- void allQueryPrev(String query, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get all changes which match an arbitrary query string. */
- void allQueryNext(String query, String pos, int limit,
- AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get the data to show {@link AccountDashboardScreen} for an account. */
- void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
-
- /** Get the changes starred by the caller. */
- @SignInRequired
- void myStarredChanges(AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get the changes with unpublished drafts by the caller. */
- @SignInRequired
- void myDraftChanges(AsyncCallback<SingleListChangeInfo> callback);
-
- /** Get the ids of all changes starred by the caller. */
- @SignInRequired
- void myStarredChangeIds(AsyncCallback<Set<Change.Id>> callback);
-
- /**
- * Add and/or remove changes from the set of starred changes of the caller.
- *
- * @param req the add and remove cluster.
- */
- @SignInRequired
- void toggleStars(ToggleStarRequest req, AsyncCallback<VoidResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeManageService.java b/src/main/java/com/google/gerrit/client/changes/ChangeManageService.java
deleted file mode 100644
index 51ff541ba9..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeManageService.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-public interface ChangeManageService extends RemoteJsonService {
- @SignInRequired
- void submit(PatchSet.Id patchSetId, AsyncCallback<ChangeDetail> callback);
-
- @SignInRequired
- void abandonChange(PatchSet.Id patchSetId, String message,
- AsyncCallback<ChangeDetail> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java b/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java
deleted file mode 100644
index b5359246e3..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeQueryResultsScreen.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.data.SingleListChangeInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-
-
-
-public class ChangeQueryResultsScreen extends AllSingleListScreen {
- private final String query;
-
- public ChangeQueryResultsScreen(final String encQuery,
- final String positionToken) {
- super("q," + encQuery, positionToken);
- query = KeyUtil.decode(encQuery);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Util.M.changeQueryWindowTitle(query));
- setPageTitle(Util.M.changeQueryPageTitle(query));
- }
-
- @Override
- protected AsyncCallback<SingleListChangeInfo> loadCallback() {
- return new GerritCallback<SingleListChangeInfo>() {
- public final void onSuccess(final SingleListChangeInfo result) {
- if (isAttached()) {
- if (result.getChanges().size() == 1) {
- final ChangeInfo c = result.getChanges().get(0);
- Gerrit.display(Link.toChange(c), new ChangeScreen(c));
- } else {
- Gerrit.setQueryString(query);
- display(result);
- ChangeQueryResultsScreen.this.display();
- }
- }
- }
- };
- }
-
- @Override
- protected void loadPrev() {
- Util.LIST_SVC.allQueryPrev(query, pos, pageSize, loadCallback());
- }
-
- @Override
- protected void loadNext() {
- Util.LIST_SVC.allQueryNext(query, pos, pageSize, loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java b/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
deleted file mode 100644
index e92ea1383c..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeScreen.java
+++ /dev/null
@@ -1,401 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.data.GitwebLink;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.CommentPanel;
-import com.google.gerrit.client.ui.ComplexDisclosurePanel;
-import com.google.gerrit.client.ui.ExpandAllCommand;
-import com.google.gerrit.client.ui.LinkMenuBar;
-import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.i18n.client.LocaleInfo;
-import com.google.gwt.user.client.ui.Anchor;
-import com.google.gwt.user.client.ui.DisclosurePanel;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.sql.Timestamp;
-import java.util.List;
-
-
-public class ChangeScreen extends Screen {
- private final Change.Id changeId;
-
- private Image starChange;
- private boolean starred;
- private PatchSet.Id currentPatchSet;
- private ChangeDescriptionBlock descriptionBlock;
- private ApprovalTable approvals;
-
- private DisclosurePanel dependenciesPanel;
- private ChangeTable dependencies;
- private ChangeTable.Section dependsOn;
- private ChangeTable.Section neededBy;
-
- private FlowPanel patchSetPanels;
-
- private Panel comments;
-
- private KeyCommandSet keysNavigation;
- private KeyCommandSet keysAction;
- private HandlerRegistration regNavigation;
- private HandlerRegistration regAction;
-
- public ChangeScreen(final Change.Id toShow) {
- changeId = toShow;
- }
-
- public ChangeScreen(final ChangeInfo c) {
- this(c.getId());
- }
-
- @Override
- public void onSignOut() {
- super.onSignOut();
- if (starChange != null) {
- starChange.setVisible(false);
- }
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refresh();
- }
-
- @Override
- protected void onUnload() {
- if (regNavigation != null) {
- regNavigation.removeHandler();
- regNavigation = null;
- }
- if (regAction != null) {
- regAction.removeHandler();
- regAction = null;
- }
- super.onUnload();
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- regNavigation = GlobalKey.add(this, keysNavigation);
- regAction = GlobalKey.add(this, keysAction);
- }
-
- public void refresh() {
- Util.DETAIL_SVC.changeDetail(changeId,
- new ScreenLoadCallback<ChangeDetail>(this) {
- @Override
- protected void preDisplay(final ChangeDetail r) {
- display(r);
- }
- });
- }
-
- private void setStarred(final boolean s) {
- if (s) {
- Gerrit.ICONS.starFilled().applyTo(starChange);
- } else {
- Gerrit.ICONS.starOpen().applyTo(starChange);
- }
- starred = s;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- addStyleName("gerrit-ChangeScreen");
-
- keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
- keysAction = new KeyCommandSet(Gerrit.C.sectionActions());
- keysNavigation.add(new DashboardKeyCommand(0, 'u', Util.C.upToDashboard()));
-
- if (Gerrit.isSignedIn()) {
- keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar()));
- keysAction.add(new PublishCommentsKeyCommand(0, 'r', Util.C
- .keyPublishComments()));
-
- starChange = Gerrit.ICONS.starOpen().createImage();
- starChange.setStyleName("gerrit-ChangeScreen-StarIcon");
- starChange.setVisible(Gerrit.isSignedIn());
- starChange.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- toggleStar();
- }
- });
- insertTitleWidget(starChange);
- }
-
- descriptionBlock = new ChangeDescriptionBlock();
- add(descriptionBlock);
-
- approvals = new ApprovalTable();
- add(approvals);
-
- dependencies = new ChangeTable();
- dependsOn = new ChangeTable.Section(Util.C.changeScreenDependsOn());
- neededBy = new ChangeTable.Section(Util.C.changeScreenNeededBy());
- dependencies.addSection(dependsOn);
- dependencies.addSection(neededBy);
-
- dependenciesPanel = new DisclosurePanel(Util.C.changeScreenDependencies());
- dependenciesPanel.setContent(dependencies);
- dependenciesPanel.setWidth("95%");
- add(dependenciesPanel);
-
- patchSetPanels = new FlowPanel();
- add(patchSetPanels);
-
- comments = new FlowPanel();
- comments.setStyleName("gerrit-ChangeComments");
- add(comments);
- }
-
- private void displayTitle(final Change.Key changeId, final String subject) {
- final StringBuilder titleBuf = new StringBuilder();
- if (LocaleInfo.getCurrentLocale().isRTL()) {
- if (subject != null) {
- titleBuf.append(subject);
- titleBuf.append(" :");
- }
- titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
- } else {
- titleBuf.append(Util.M.changeScreenTitleId(changeId.abbreviate()));
- if (subject != null) {
- titleBuf.append(": ");
- titleBuf.append(subject);
- }
- }
- setPageTitle(titleBuf.toString());
- }
-
- void display(final ChangeDetail detail) {
- displayTitle(detail.getChange().getKey(), detail.getChange().getSubject());
-
- if (starChange != null) {
- setStarred(detail.isStarred());
- }
-
- dependencies.setAccountInfoCache(detail.getAccounts());
- approvals.setAccountInfoCache(detail.getAccounts());
-
- descriptionBlock.display(detail.getChange(), detail
- .getCurrentPatchSetDetail().getInfo(), detail.getAccounts());
- dependsOn.display(detail.getDependsOn());
- neededBy.display(detail.getNeededBy());
- approvals.display(detail.getChange(), detail.getMissingApprovals(), detail
- .getApprovals());
-
- addPatchSets(detail);
- addComments(detail);
-
- // If any dependency change is still open, show our dependency list.
- //
- boolean depsOpen = false;
- if (!detail.getChange().getStatus().isClosed()
- && detail.getDependsOn() != null) {
- for (final ChangeInfo ci : detail.getDependsOn()) {
- if (ci.getStatus() != Change.Status.MERGED) {
- depsOpen = true;
- break;
- }
- }
- }
-
- dependenciesPanel.setOpen(depsOpen);
- }
-
- private void addPatchSets(final ChangeDetail detail) {
- patchSetPanels.clear();
-
- final PatchSet currps = detail.getCurrentPatchSet();
- final GitwebLink gw = Gerrit.getConfig().getGitwebLink();
- for (final PatchSet ps : detail.getPatchSets()) {
- final ComplexDisclosurePanel panel =
- new ComplexDisclosurePanel(Util.M.patchSetHeader(ps.getPatchSetId()),
- ps == currps);
- final PatchSetPanel psp = new PatchSetPanel(this, detail, ps);
- panel.setContent(psp);
-
- final InlineLabel revtxt = new InlineLabel(ps.getRevision().get() + " ");
- revtxt.addStyleName("gerrit-PatchSetRevision");
- panel.getHeader().add(revtxt);
- if (gw != null) {
- final Anchor revlink =
- new Anchor("(gitweb)", false, gw.toRevision(detail.getChange()
- .getProject(), ps));
- revlink.addStyleName("gerrit-PatchSetLink");
- panel.getHeader().add(revlink);
- }
-
- if (ps == currps) {
- psp.ensureLoaded(detail.getCurrentPatchSetDetail());
- } else {
- panel.addOpenHandler(psp);
- }
- add(panel);
- patchSetPanels.add(panel);
- }
- currentPatchSet = currps.getId();
- }
-
- private void addComments(final ChangeDetail detail) {
- comments.clear();
-
- final Label hdr = new Label(Util.C.changeScreenComments());
- hdr.setStyleName("gerrit-BlockHeader");
- comments.add(hdr);
-
- final AccountInfoCache accts = detail.getAccounts();
- final List<ChangeMessage> msgList = detail.getMessages();
- if (msgList.size() > 1) {
- comments.add(messagesMenuBar());
- }
-
- final long AGE = 7 * 24 * 60 * 60 * 1000L;
- final Timestamp aged = new Timestamp(System.currentTimeMillis() - AGE);
-
- for (int i = 0; i < msgList.size(); i++) {
- final ChangeMessage msg = msgList.get(i);
-
- final AccountInfo author;
- if (msg.getAuthor() != null) {
- author = accts.get(msg.getAuthor());
- } else {
- final Account gerrit = new Account(null);
- gerrit.setFullName(Util.C.messageNoAuthor());
- author = new AccountInfo(gerrit);
- }
-
- boolean isRecent;
- if (i == msgList.size() - 1) {
- isRecent = true;
- } else {
- // TODO Instead of opening messages by strict age, do it by "unread"?
- isRecent = msg.getWrittenOn().after(aged);
- }
-
- final CommentPanel cp =
- new CommentPanel(author, msg.getWrittenOn(), msg.getMessage());
- cp.setRecent(isRecent);
- if (i == msgList.size() - 1) {
- cp.addStyleName("gerrit-CommentPanel-Last");
- cp.setOpen(true);
- }
- comments.add(cp);
- }
-
- if (msgList.size() > 1) {
- comments.add(messagesMenuBar());
- }
- comments.setVisible(msgList.size() > 0);
- }
-
- private LinkMenuBar messagesMenuBar() {
- final Panel c = comments;
- final LinkMenuBar m = new LinkMenuBar();
- m.addItem(Util.C.messageExpandRecent(), new ExpandAllCommand(c, true) {
- @Override
- protected void expand(final CommentPanel w) {
- w.setOpen(w.isRecent());
- }
- });
- m.addItem(Util.C.messageExpandAll(), new ExpandAllCommand(c, true));
- m.addItem(Util.C.messageCollapseAll(), new ExpandAllCommand(c, false));
- return m;
- }
-
- private void toggleStar() {
- final boolean prior = starred;
- setStarred(!prior);
-
- final ToggleStarRequest req = new ToggleStarRequest();
- req.toggle(changeId, starred);
- Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- super.onFailure(caught);
- setStarred(prior);
- }
- });
- }
-
- public class DashboardKeyCommand extends KeyCommand {
- public DashboardKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (Gerrit.isSignedIn()) {
- Gerrit.display(Link.MINE, true);
- } else {
- Gerrit.display(Link.ALL_OPEN, true);
- }
- }
- }
-
- public class StarKeyCommand extends NeedsSignInKeyCommand {
- public StarKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- toggleStar();
- }
- }
-
- public class PublishCommentsKeyCommand extends NeedsSignInKeyCommand {
- public PublishCommentsKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- Gerrit.display("change,publish," + currentPatchSet.toString(),
- new PublishCommentScreen(currentPatchSet));
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
deleted file mode 100644
index 664f1ade39..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ /dev/null
@@ -1,489 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import static com.google.gerrit.client.FormatUtil.shortFormat;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.data.ApprovalSummary;
-import com.google.gerrit.client.data.ApprovalSummarySet;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountDashboardLink;
-import com.google.gerrit.client.ui.ChangeLink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
-import com.google.gerrit.client.ui.ProjectLink;
-import com.google.gwt.dom.client.Element;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.AbstractImagePrototype;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class ChangeTable extends NavigationTable<ChangeInfo> {
- private static final String S_C_ID = "C_ID";
- private static final String S_C_SUBJECT = "C_SUBJECT";
- private static final String S_C_PROJECT = "C_PROJECT";
- private static final String S_C_LAST_UPDATE = "C_LAST_UPDATE";
- private static final String S_C_APPROVAL = "C_APPROVAL";
- private static final String S_SECTION_HEADER = "SectionHeader";
- private static final String S_EMPTY_SECTION = "EmptySection";
- private static final String S_NEEDS_REVIEW = "NeedsReview";
-
- private static final int C_STAR = 1;
- private static final int C_ID = 2;
- private static final int C_SUBJECT = 3;
- private static final int C_OWNER = 4;
- private static final int C_PROJECT = 5;
- private static final int C_BRANCH = 6;
- private static final int C_LAST_UPDATE = 7;
- private static final int BASE_COLUMNS = 8;
-
- private final List<Section> sections;
- private AccountInfoCache accountCache = AccountInfoCache.empty();
- private final List<ApprovalType> approvalTypes;
- private final int columns;
-
- public ChangeTable() {
- this(false);
- }
-
- public ChangeTable(boolean showApprovals) {
- approvalTypes = Gerrit.getConfig().getApprovalTypes().getApprovalTypes();
- if (showApprovals) {
- columns = BASE_COLUMNS + approvalTypes.size();
- } else {
- columns = BASE_COLUMNS;
- }
-
- keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.changeTablePrev()));
- keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.changeTableNext()));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.changeTableOpen()));
- keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
- .changeTableOpen()));
-
- if (Gerrit.isSignedIn()) {
- keysAction.add(new StarKeyCommand(0, 's', Util.C.changeTableStar()));
- }
-
- sections = new ArrayList<Section>();
- table.setText(0, C_STAR, "");
- table.setText(0, C_ID, Util.C.changeTableColumnID());
- table.setText(0, C_SUBJECT, Util.C.changeTableColumnSubject());
- table.setText(0, C_OWNER, Util.C.changeTableColumnOwner());
- table.setText(0, C_PROJECT, Util.C.changeTableColumnProject());
- table.setText(0, C_BRANCH, Util.C.changeTableColumnBranch());
- table.setText(0, C_LAST_UPDATE, Util.C.changeTableColumnLastUpdate());
- for (int i = BASE_COLUMNS; i < columns; i++) {
- final ApprovalType type = approvalTypes.get(i - BASE_COLUMNS);
- final ApprovalCategory cat = type.getCategory();
- String text = cat.getAbbreviatedName();
- if (text == null) {
- text = cat.getName();
- }
- table.setText(0, i, text);
- if (text != null) {
- table.getCellFormatter().getElement(0, i).setTitle(cat.getName());
- }
- }
-
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.addStyleName(0, C_STAR, S_ICON_HEADER);
- fmt.addStyleName(0, C_ID, S_C_ID);
- for (int i = C_ID; i < columns; i++) {
- fmt.addStyleName(0, i, S_DATA_HEADER);
- }
-
- table.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell == null) {
- return;
- }
- if (cell.getCellIndex() == C_STAR) {
- onStarClick(cell.getRowIndex());
- } else if (cell.getCellIndex() == C_OWNER) {
- // Don't do anything.
- } else if (getRowItem(cell.getRowIndex()) != null) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
- }
-
- protected void onStarClick(final int row) {
- final ChangeInfo c = getRowItem(row);
- if (c != null && Gerrit.isSignedIn()) {
- final boolean prior = c.isStarred();
- c.setStarred(!prior);
- setStar(row, c);
-
- final ToggleStarRequest req = new ToggleStarRequest();
- req.toggle(c.getId(), c.isStarred());
- Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- super.onFailure(caught);
- c.setStarred(prior);
- setStar(row, c);
- }
- });
- }
- }
-
- @Override
- protected Object getRowItemKey(final ChangeInfo item) {
- return item.getId();
- }
-
- @Override
- protected void onOpenRow(final int row) {
- final ChangeInfo c = getRowItem(row);
- Gerrit.display(Link.toChange(c), new ChangeScreen(c));
- }
-
- private void insertNoneRow(final int row) {
- insertRow(row);
- table.setText(row, 0, Util.C.changeTableNone());
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(row, 0, columns);
- fmt.setStyleName(row, 0, S_EMPTY_SECTION);
- }
-
- private void insertChangeRow(final int row) {
- insertRow(row);
- applyDataRowStyle(row);
- }
-
- @Override
- protected void applyDataRowStyle(final int row) {
- super.applyDataRowStyle(row);
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, C_STAR, S_ICON_CELL);
- for (int i = C_ID; i < columns; i++) {
- fmt.addStyleName(row, i, S_DATA_CELL);
- }
- fmt.addStyleName(row, C_ID, S_C_ID);
- fmt.addStyleName(row, C_SUBJECT, S_C_SUBJECT);
- fmt.addStyleName(row, C_PROJECT, S_C_PROJECT);
- fmt.addStyleName(row, C_BRANCH, S_C_PROJECT);
- fmt.addStyleName(row, C_LAST_UPDATE, S_C_LAST_UPDATE);
- for (int i = BASE_COLUMNS; i < columns; i++) {
- fmt.addStyleName(row, i, S_C_APPROVAL);
- }
- }
-
- private void populateChangeRow(final int row, final ChangeInfo c) {
- final String idstr = c.getKey().abbreviate();
- table.setWidget(row, C_ARROW, null);
- if (Gerrit.isSignedIn()) {
- setStar(row, c);
- }
- table.setWidget(row, C_ID, new TableChangeLink(idstr, c));
-
- String s = c.getSubject();
- if (s.length() > 80) {
- s = s.substring(0, 80);
- }
- if (c.getStatus() != null && c.getStatus() != Change.Status.NEW) {
- s += " (" + c.getStatus().name() + ")";
- }
- table.setWidget(row, C_SUBJECT, new TableChangeLink(s, c));
- table.setWidget(row, C_OWNER, link(c.getOwner()));
- table.setWidget(row, C_PROJECT,
- new ProjectLink(c.getProject().getKey(), c.getStatus()));
- table.setText(row, C_BRANCH, c.getBranch());
- table.setText(row, C_LAST_UPDATE, shortFormat(c.getLastUpdatedOn()));
- setRowItem(row, c);
- }
-
- private AccountDashboardLink link(final Account.Id id) {
- return AccountDashboardLink.link(accountCache, id);
- }
-
- private void setStar(final int row, final ChangeInfo c) {
- final AbstractImagePrototype star;
- if (c.isStarred()) {
- star = Gerrit.ICONS.starFilled();
- } else {
- star = Gerrit.ICONS.starOpen();
- }
-
- final Widget i = table.getWidget(row, C_STAR);
- if (i instanceof Image) {
- star.applyTo((Image) i);
- } else {
- table.setWidget(row, C_STAR, star.createImage());
- }
- }
-
- public void addSection(final Section s) {
- assert s.parent == null;
-
- if (s.titleText != null) {
- s.titleRow = table.getRowCount();
- table.setText(s.titleRow, 0, s.titleText);
- final FlexCellFormatter fmt = table.getFlexCellFormatter();
- fmt.setColSpan(s.titleRow, 0, columns);
- fmt.addStyleName(s.titleRow, 0, S_SECTION_HEADER);
- } else {
- s.titleRow = -1;
- }
-
- s.parent = this;
- s.dataBegin = table.getRowCount();
- insertNoneRow(s.dataBegin);
- sections.add(s);
- }
-
- public void setAccountInfoCache(final AccountInfoCache aic) {
- assert aic != null;
- accountCache = aic;
- }
-
- private int insertRow(final int beforeRow) {
- for (final Section s : sections) {
- if (beforeRow <= s.titleRow) {
- s.titleRow++;
- }
- if (beforeRow < s.dataBegin) {
- s.dataBegin++;
- }
- }
- return table.insertRow(beforeRow);
- }
-
- private void removeRow(final int row) {
- for (final Section s : sections) {
- if (row < s.titleRow) {
- s.titleRow--;
- }
- if (row < s.dataBegin) {
- s.dataBegin--;
- }
- }
- table.removeRow(row);
- }
-
- private void displayApprovals(final int row, final ApprovalSummary summary,
- final AccountInfoCache aic, final boolean highlightUnreviewed) {
- final CellFormatter fmt = table.getCellFormatter();
- final Map<ApprovalCategory.Id, PatchSetApproval> approvals =
- summary.getApprovalMap();
- int col = BASE_COLUMNS;
- boolean haveReview = false;
-
- for (final ApprovalType type : approvalTypes) {
- final PatchSetApproval ca = approvals.get(type.getCategory().getId());
-
- fmt.removeStyleName(row, col, "negscore");
- fmt.removeStyleName(row, col, "posscore");
-
- if (ca == null || ca.getValue() == 0) {
- table.clearCell(row, col);
-
- } else {
- haveReview = true;
-
- if (type.isMaxNegative(ca)) {
- table.setWidget(row, col, Gerrit.ICONS.redNot().createImage());
-
- } else if (type.isMaxPositive(ca)) {
- table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
-
- } else {
- String vstr = String.valueOf(ca.getValue());
- if (ca.getValue() > 0) {
- vstr = "+" + vstr;
- fmt.addStyleName(row, col, "posscore");
- } else {
- fmt.addStyleName(row, col, "negscore");
- }
- table.setText(row, col, vstr);
- }
-
- final ApprovalCategoryValue acv = type.getValue(ca);
- final AccountInfo ai = aic.get(ca.getAccountId());
-
- // Some web browsers ignore the embedded newline; some like it;
- // so we include a space before the newline to accommodate both.
- //
- fmt.getElement(row, col).setTitle(
- acv.getName() + " \nby " + FormatUtil.nameEmail(ai));
- }
-
- col++;
- }
-
- final Element tr = DOM.getParent(fmt.getElement(row, 0));
- UIObject.setStyleName(tr, S_NEEDS_REVIEW, !haveReview
- && highlightUnreviewed);
- }
-
- GerritCallback<ApprovalSummarySet> approvalFormatter(final int dataBegin,
- final int rows, final boolean highlightUnreviewed) {
- return new GerritCallback<ApprovalSummarySet>() {
- @Override
- public void onSuccess(final ApprovalSummarySet as) {
- Map<Change.Id, ApprovalSummary> ids = as.getSummaryMap();
- AccountInfoCache aic = as.getAccountInfoCache();
- for (int row = dataBegin; row < dataBegin + rows; row++) {
- final ChangeInfo c = getRowItem(row);
- if (ids.containsKey(c.getId())) {
- displayApprovals(row, ids.get(c.getId()), aic, highlightUnreviewed);
- }
- }
- }
- };
- }
-
- public class StarKeyCommand extends NeedsSignInKeyCommand {
- public StarKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- onStarClick(getCurrentRow());
- }
- }
-
- private final class TableChangeLink extends ChangeLink {
- private TableChangeLink(final String text, final ChangeInfo c) {
- super(text, c);
- }
-
- @Override
- public void go() {
- movePointerTo(id);
- super.go();
- }
- }
-
- public enum ApprovalViewType {
- NONE, USER, STRONGEST
- }
-
- public static class Section {
- String titleText;
-
- ChangeTable parent;
- final ApprovalViewType viewType;
- final Account.Id ownerId;
- int titleRow = -1;
- int dataBegin;
- int rows;
-
- public Section() {
- this(null, ApprovalViewType.NONE, null);
- }
-
- public Section(final String titleText) {
- this(titleText, ApprovalViewType.NONE, null);
- }
-
- public Section(final String titleText, final ApprovalViewType view,
- final Account.Id owner) {
- setTitleText(titleText);
- viewType = view;
- ownerId = owner;
- }
-
- public void setTitleText(final String text) {
- titleText = text;
- if (titleRow >= 0) {
- parent.table.setText(titleRow, 0, titleText);
- }
- }
-
- public void display(final List<ChangeInfo> changeList) {
- final int sz = changeList != null ? changeList.size() : 0;
- final boolean hadData = rows > 0;
-
- if (hadData) {
- while (sz < rows) {
- parent.removeRow(dataBegin);
- rows--;
- }
- }
-
- if (sz == 0) {
- if (hadData) {
- parent.insertNoneRow(dataBegin);
- }
- } else {
- Set<Change.Id> cids = new HashSet<Change.Id>();
-
- if (!hadData) {
- parent.removeRow(dataBegin);
- }
-
- while (rows < sz) {
- parent.insertChangeRow(dataBegin + rows);
- rows++;
- }
-
- for (int i = 0; i < sz; i++) {
- ChangeInfo c = changeList.get(i);
- parent.populateChangeRow(dataBegin + i, c);
- cids.add(c.getId());
- }
-
- switch (viewType) {
- case NONE:
- break;
- case USER:
- PatchUtil.DETAIL_SVC.userApprovals(cids, ownerId, parent
- .approvalFormatter(dataBegin, rows, true));
- break;
- case STRONGEST:
- PatchUtil.DETAIL_SVC.strongestApprovals(cids, parent
- .approvalFormatter(dataBegin, rows, false));
- break;
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/MessagePanel.java b/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
deleted file mode 100644
index 4b3487e3d8..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/MessagePanel.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-public class MessagePanel extends Composite {
- boolean isRecent;
-
- public MessagePanel(final ChangeMessage msg) {
- final Widget l =
- new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify()
- .replaceAll(Gerrit.getConfig().getCommentLinks()).toBlockWidget();
- l.setStyleName("gerrit-ChangeMessage-Message");
- initWidget(l);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java b/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java
deleted file mode 100644
index a818d86f2e..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/MineDraftsScreen.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-
-
-public class MineDraftsScreen extends MineSingleListScreen {
- public MineDraftsScreen() {
- super(Link.MINE_DRAFTS);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Gerrit.C.menyMyDrafts());
- setPageTitle(Util.C.draftsHeading());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.LIST_SVC.myDraftChanges(loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java b/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java
deleted file mode 100644
index 09e550148f..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/MineSingleListScreen.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.SingleListChangeInfo;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-
-public abstract class MineSingleListScreen extends AccountScreen {
- private final String anchor;
- private ChangeTable table;
- private ChangeTable.Section drafts;
-
- protected MineSingleListScreen(final String historyToken) {
- anchor = historyToken;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- table = new ChangeTable();
- drafts = new ChangeTable.Section();
-
- table.addSection(drafts);
- table.setSavePointerId(anchor);
-
- add(table);
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- table.setRegisterKeys(true);
- }
-
- protected AsyncCallback<SingleListChangeInfo> loadCallback() {
- return new ScreenLoadCallback<SingleListChangeInfo>(this) {
- @Override
- protected void preDisplay(final SingleListChangeInfo result) {
- display(result);
- }
- };
- }
-
- private void display(final SingleListChangeInfo result) {
- table.setAccountInfoCache(result.getAccounts());
- drafts.display(result.getChanges());
- table.finishDisplay();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java b/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java
deleted file mode 100644
index c462f66af0..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/MineStarredScreen.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-
-
-public class MineStarredScreen extends MineSingleListScreen {
- public MineStarredScreen() {
- super(Link.MINE_STARRED);
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- setWindowTitle(Gerrit.C.menuMyStarredChanges());
- setPageTitle(Util.C.starredHeading());
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.LIST_SVC.myStarredChanges(loadCallback());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java b/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
deleted file mode 100644
index a1b65e55b0..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/PatchSetPanel.java
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.data.PatchSetDetail;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.UserIdentity;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.AccountDashboardLink;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.logical.shared.OpenEvent;
-import com.google.gwt.event.logical.shared.OpenHandler;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.DisclosurePanel;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
-
-import java.util.Collections;
-import java.util.Set;
-
-class PatchSetPanel extends Composite implements OpenHandler<DisclosurePanel> {
- private static final int R_AUTHOR = 0;
- private static final int R_COMMITTER = 1;
- private static final int R_DOWNLOAD = 2;
- private static final int R_CNT = 3;
-
- private final ChangeScreen changeScreen;
- private final ChangeDetail changeDetail;
- private final PatchSet patchSet;
- private final FlowPanel body;
-
- private Grid infoTable;
- private Panel actionsPanel;
- private PatchTable patchTable;
-
- PatchSetPanel(final ChangeScreen parent, final ChangeDetail detail,
- final PatchSet ps) {
- changeScreen = parent;
- changeDetail = detail;
- patchSet = ps;
- body = new FlowPanel();
- initWidget(body);
- }
-
- /**
- * Display the table showing the Author, Committer and Download links,
- * followed by the action buttons.
- */
- public void ensureLoaded(final PatchSetDetail detail) {
- infoTable = new Grid(R_CNT, 2);
- infoTable.setStyleName("gerrit-InfoBlock");
- infoTable.addStyleName("gerrit-PatchSetInfoBlock");
-
- initRow(R_AUTHOR, Util.C.patchSetInfoAuthor());
- initRow(R_COMMITTER, Util.C.patchSetInfoCommitter());
- initRow(R_DOWNLOAD, Util.C.patchSetInfoDownload());
-
- final CellFormatter itfmt = infoTable.getCellFormatter();
- itfmt.addStyleName(0, 0, "topmost");
- itfmt.addStyleName(0, 1, "topmost");
- itfmt.addStyleName(R_CNT - 1, 0, "bottomheader");
- itfmt.addStyleName(R_AUTHOR, 1, "useridentity");
- itfmt.addStyleName(R_COMMITTER, 1, "useridentity");
- itfmt.addStyleName(R_DOWNLOAD, 1, "command");
-
- final PatchSetInfo info = detail.getInfo();
- displayUserIdentity(R_AUTHOR, info.getAuthor());
- displayUserIdentity(R_COMMITTER, info.getCommitter());
- displayDownload();
-
-
- patchTable = new PatchTable();
- patchTable.setSavePointerId("PatchTable " + patchSet.getId());
- patchTable.display(info.getKey(), detail.getPatches());
-
- body.add(infoTable);
-
- actionsPanel = new FlowPanel();
- actionsPanel.setStyleName("gerrit-PatchSetActions");
- body.add(actionsPanel);
- if (Gerrit.isSignedIn()) {
- populateCommentAction();
- if (changeDetail.isCurrentPatchSet(detail)) {
- populateActions(detail);
- }
- }
- body.add(patchTable);
- }
-
- private void displayDownload() {
- final Branch.NameKey branchKey = changeDetail.getChange().getDest();
- final Project.NameKey projectKey = changeDetail.getChange().getProject();
- final String projectName = projectKey.get();
- final FlowPanel downloads = new FlowPanel();
-
- if (Gerrit.getConfig().isUseRepoDownload()) {
- // This site prefers usage of the 'repo' tool, so suggest
- // that for easy fetch.
- //
- final StringBuilder r = new StringBuilder();
- r.append("repo download ");
- r.append(projectName);
- r.append(" ");
- r.append(changeDetail.getChange().getChangeId());
- r.append("/");
- r.append(patchSet.getPatchSetId());
- downloads.add(new CopyableLabel(r.toString()));
- }
-
- if (changeDetail.isAllowsAnonymous()
- && Gerrit.getConfig().getGitDaemonUrl() != null) {
- // Anonymous Git is claimed to be available, and this project
- // isn't secured. The anonymous Git daemon will be much more
- // efficient than our own SSH daemon, so prefer offering it.
- //
- final StringBuilder r = new StringBuilder();
- r.append("git pull ");
- r.append(Gerrit.getConfig().getGitDaemonUrl());
- r.append(projectName);
- r.append(" ");
- r.append(patchSet.getRefName());
- downloads.add(new CopyableLabel(r.toString()));
-
- } else if (Gerrit.isSignedIn() && Gerrit.getUserAccount() != null
- && Gerrit.getConfig().getSshdAddress() != null
- && Gerrit.getUserAccount().getSshUserName() != null
- && Gerrit.getUserAccount().getSshUserName().length() > 0) {
- // The user is signed in and anonymous access isn't allowed.
- // Use our SSH daemon URL as its the only way they can get
- // to the project (that we know of anyway).
- //
- final String sshAddr = Gerrit.getConfig().getSshdAddress();
- final StringBuilder r = new StringBuilder();
- r.append("git pull ssh://");
- r.append(Gerrit.getUserAccount().getSshUserName());
- r.append("@");
- if (sshAddr.startsWith(":") || "".equals(sshAddr)) {
- r.append(Window.Location.getHostName());
- }
- r.append(sshAddr);
- r.append("/");
- r.append(projectName);
- r.append(" ");
- r.append(patchSet.getRefName());
- downloads.add(new CopyableLabel(r.toString()));
- }
-
- infoTable.setWidget(R_DOWNLOAD, 1, downloads);
- }
-
- private void displayUserIdentity(final int row, final UserIdentity who) {
- if (who == null) {
- infoTable.clearCell(row, 1);
- return;
- }
-
- final FlowPanel fp = new FlowPanel();
- fp.setStyleName("gerrit-PatchSetUserIdentity");
- if (who.getName() != null) {
- final Account.Id aId = who.getAccount();
- if (aId != null) {
- fp.add(new AccountDashboardLink(who.getName(), aId));
- } else {
- final InlineLabel lbl = new InlineLabel(who.getName());
- lbl.setStyleName("gerrit-AccountName");
- fp.add(lbl);
- }
- }
- if (who.getEmail() != null) {
- fp.add(new InlineLabel("<" + who.getEmail() + ">"));
- }
- if (who.getDate() != null) {
- fp.add(new InlineLabel(FormatUtil.mediumFormat(who.getDate())));
- }
- infoTable.setWidget(row, 1, fp);
- }
-
- private void populateActions(final PatchSetDetail detail) {
- final boolean isOpen = changeDetail.getChange().getStatus().isOpen();
- Set<ApprovalCategory.Id> allowed = changeDetail.getCurrentActions();
- if (allowed == null) {
- allowed = Collections.emptySet();
- }
-
- if (isOpen && allowed.contains(ApprovalCategory.SUBMIT)) {
- final Button b =
- new Button(Util.M
- .submitPatchSet(detail.getPatchSet().getPatchSetId()));
- b.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- b.setEnabled(false);
- Util.MANAGE_SVC.submit(patchSet.getId(),
- new GerritCallback<ChangeDetail>() {
- public void onSuccess(ChangeDetail result) {
- onSubmitResult(result);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- b.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
- });
- actionsPanel.add(b);
- }
-
- if (changeDetail.canAbandon()) {
- final Button b = new Button(Util.C.buttonAbandonChangeBegin());
- b.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- new AbandonChangeDialog(patchSet.getId(),
- new AsyncCallback<ChangeDetail>() {
- public void onSuccess(ChangeDetail result) {
- changeScreen.display(result);
- }
-
- public void onFailure(Throwable caught) {
- b.setEnabled(true);
- }
- }).center();
- }
- });
- actionsPanel.add(b);
- }
- }
-
- private void populateCommentAction() {
- final Button b = new Button(Util.C.buttonPublishCommentsBegin());
- b.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- Gerrit.display("change,publish," + patchSet.getId().toString(),
- new PublishCommentScreen(patchSet.getId()));
- }
- });
- actionsPanel.add(b);
- }
-
- @Override
- public void onOpen(final OpenEvent<DisclosurePanel> event) {
- if (infoTable == null) {
- Util.DETAIL_SVC.patchSetDetail(patchSet.getId(),
- new GerritCallback<PatchSetDetail>() {
- public void onSuccess(final PatchSetDetail result) {
- ensureLoaded(result);
- }
- });
- }
- }
-
- private void initRow(final int row, final String name) {
- infoTable.setText(row, 0, name);
- infoTable.getCellFormatter().addStyleName(row, 0, "header");
- }
-
- private void onSubmitResult(final ChangeDetail result) {
- if (result.getChange().getStatus() == Change.Status.NEW) {
- // The submit failed. Try to locate the message and display
- // it to the user, it should be the last one created by Gerrit.
- //
- ChangeMessage msg = null;
- if (result.getMessages() != null && result.getMessages().size() > 0) {
- for (int i = result.getMessages().size() - 1; i >= 0; i--) {
- if (result.getMessages().get(i).getAuthor() == null) {
- msg = result.getMessages().get(i);
- break;
- }
- }
- }
-
- if (msg != null) {
- new SubmitFailureDialog(result, msg).center();
- }
- }
- changeScreen.display(result);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/PatchSetPublishDetail.java b/src/main/java/com/google/gerrit/client/changes/PatchSetPublishDetail.java
deleted file mode 100644
index 8e66eb69ad..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/PatchSetPublishDetail.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class PatchSetPublishDetail {
- protected AccountInfoCache accounts;
- protected PatchSetInfo patchSetInfo;
- protected Change change;
- protected List<PatchLineComment> drafts;
- protected Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
- protected Map<ApprovalCategory.Id, PatchSetApproval> given;
-
- public Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> getAllowed() {
- return allowed;
- }
-
- public void setAllowed(
- Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed) {
- this.allowed = allowed;
- }
-
- public Map<ApprovalCategory.Id, PatchSetApproval> getGiven() {
- return given;
- }
-
- public void setGiven(Map<ApprovalCategory.Id, PatchSetApproval> given) {
- this.given = given;
- }
-
- public void setAccounts(AccountInfoCache accounts) {
- this.accounts = accounts;
- }
-
- public void setPatchSetInfo(PatchSetInfo patchSetInfo) {
- this.patchSetInfo = patchSetInfo;
- }
-
- public void setChange(Change change) {
- this.change = change;
- }
-
- public void setDrafts(List<PatchLineComment> drafts) {
- this.drafts = drafts;
- }
-
- public AccountInfoCache getAccounts() {
- return accounts;
- }
-
- public Change getChange() {
- return change;
- }
-
- public PatchSetInfo getPatchSetInfo() {
- return patchSetInfo;
- }
-
- public List<PatchLineComment> getDrafts() {
- return drafts;
- }
-
- public boolean isAllowed(final ApprovalCategory.Id id) {
- final Set<ApprovalCategoryValue.Id> s = getAllowed(id);
- return s != null && !s.isEmpty();
- }
-
- public Set<ApprovalCategoryValue.Id> getAllowed(final ApprovalCategory.Id id) {
- return allowed.get(id);
- }
-
- public PatchSetApproval getChangeApproval(final ApprovalCategory.Id id) {
- return given.get(id);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/src/main/java/com/google/gerrit/client/changes/PatchTable.java
deleted file mode 100644
index 9e0e3d3aa1..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/PatchTable.java
+++ /dev/null
@@ -1,560 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.patches.PatchScreen;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Patch.Key;
-import com.google.gerrit.client.ui.DirectScreenLink;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.PatchLink;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.IncrementalCommand;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.HTMLTable.Cell;
-import com.google.gwtexpui.progress.client.ProgressBar;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtorm.client.KeyUtil;
-
-import java.util.List;
-
-public class PatchTable extends Composite {
- private final FlowPanel myBody;
- private PatchSet.Id psid;
- private Command onLoadCommand;
- private MyTable myTable;
- private String savePointerId;
- private List<Patch> patchList;
-
- public PatchTable() {
- myBody = new FlowPanel();
- initWidget(myBody);
- }
-
- public void display(final PatchSet.Id id, final List<Patch> list) {
- psid = id;
- myTable = null;
- patchList = list;
-
- final DisplayCommand cmd = new DisplayCommand(list);
- if (cmd.execute()) {
- cmd.initMeter();
- DeferredCommand.addCommand(cmd);
- } else {
- cmd.showTable();
- }
- }
-
- public void setSavePointerId(final String id) {
- savePointerId = id;
- }
-
- public boolean isLoaded() {
- return myTable != null;
- }
-
- public void onTableLoaded(final Command cmd) {
- if (myTable != null) {
- cmd.execute();
- } else {
- onLoadCommand = cmd;
- }
- }
-
- public void setRegisterKeys(final boolean on) {
- myTable.setRegisterKeys(on);
- }
-
- public void movePointerTo(final Patch.Key k) {
- myTable.movePointerTo(k);
- }
-
- public void notifyDraftDelta(final Patch.Key k, final int delta) {
- if (myTable != null) {
- myTable.notifyDraftDelta(k, delta);
- }
- }
-
- /**
- * @return a link to the previous file in this patch set, or null.
- */
- public DirectScreenLink getPreviousPatchLink(int index, PatchScreen.Type patchType) {
- if (0 < index)
- return createLink(index - 1, patchType, SafeHtml.asis(Util.C
- .prevPatchLinkIcon()), null);
- return null;
- }
-
- /**
- * @return a link to the next file in this patch set, or null.
- */
- public DirectScreenLink getNextPatchLink(int index, PatchScreen.Type patchType) {
- if (index < patchList.size() - 1)
- return createLink(index + 1, patchType, null, SafeHtml.asis(Util.C
- .nextPatchLinkIcon()));
- return null;
- }
-
- /**
- * @return a link to the the given patch.
- * @param index The patch to link to
- * @param patchType The type of patch display
- * @param before A string to display at the beginning of the href text
- * @param after A string to display at the end of the href text
- */
- private PatchLink createLink(int index, PatchScreen.Type patchType,
- SafeHtml before, SafeHtml after) {
- Patch patch = patchList.get(index);
- Key thisKey = patch.getKey();
- PatchLink link;
- if (patchType == PatchScreen.Type.SIDE_BY_SIDE
- && patch.getPatchType() == Patch.PatchType.UNIFIED) {
- link = new PatchLink.SideBySide("", thisKey, index, this);
- } else {
- link = new PatchLink.Unified("", thisKey, index, this);
- }
- SafeHtmlBuilder text = new SafeHtmlBuilder();
- text.append(before);
- text.append(getFileNameOnly(patch));
- text.append(after);
- SafeHtml.set(link, text);
- return link;
- }
-
- private static String getFileNameOnly(Patch patch) {
- // Note: use '/' here and not File.pathSeparator since git paths
- // are always separated by /
- //
- String fileName = patch.getFileName();
- int s = fileName.lastIndexOf('/');
- if (s >= 0) {
- fileName = fileName.substring(s + 1);
- }
- return fileName;
- }
-
- /**
- * Update the reviewed status for the given patch.
- */
- public void updateReviewedStatus(Patch.Key patchKey, boolean reviewed) {
- if (myTable != null) {
- myTable.updateReviewedStatus(patchKey, reviewed);
- }
- }
-
- private class MyTable extends NavigationTable<Patch> {
- private static final int C_PATH = 2;
- private static final int C_DRAFT = 3;
- private static final int C_SIDEBYSIDE = 4;
-
- MyTable() {
- keysNavigation.add(new PrevKeyCommand(0, 'k', Util.C.patchTablePrev()));
- keysNavigation.add(new NextKeyCommand(0, 'j', Util.C.patchTableNext()));
- keysNavigation.add(new OpenKeyCommand(0, 'o', Util.C.patchTableOpen()));
- keysNavigation.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, Util.C
- .patchTableOpen()));
-
- table.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(final ClickEvent event) {
- final Cell cell = table.getCellForEvent(event);
- if (cell != null && cell.getRowIndex() > 0) {
- movePointerTo(cell.getRowIndex());
- }
- }
- });
- setSavePointerId(PatchTable.this.savePointerId);
- }
-
- void updateReviewedStatus(final Patch.Key patchKey, boolean reviewed) {
- final int row = findRow(patchKey);
- if (0 <= row) {
- final Patch patch = getRowItem(row);
- if (patch != null) {
- patch.setReviewedByCurrentUser(reviewed);
-
- int col = C_SIDEBYSIDE + 2;
- if (patch.getPatchType() == Patch.PatchType.BINARY) {
- col = C_SIDEBYSIDE + 3;
- }
-
- if (reviewed) {
- table.setWidget(row, col, Gerrit.ICONS.greenCheck().createImage());
- } else {
- table.clearCell(row, col);
- }
- }
- }
- }
-
- void notifyDraftDelta(final Patch.Key key, final int delta) {
- final int row = findRow(key);
- if (0 <= row) {
- final Patch p = getRowItem(row);
- if (p != null) {
- p.setDraftCount(p.getDraftCount() + delta);
- final SafeHtmlBuilder m = new SafeHtmlBuilder();
- appendCommentCount(m, p);
- SafeHtml.set(table, row, C_DRAFT, m);
- }
- }
- }
-
- @Override
- public void resetHtml(final SafeHtml html) {
- super.resetHtml(html);
- }
-
- @Override
- public void movePointerTo(Object oldId) {
- super.movePointerTo(oldId);
- }
-
- void initializeRow(int row) {
- Patch patch = PatchTable.this.patchList.get(row - 1);
- setRowItem(row, patch);
-
- Widget nameCol;
- if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
- nameCol = new PatchLink.SideBySide(patch.getFileName(), patch.getKey(), row - 1,
- PatchTable.this);
- } else {
- nameCol = new PatchLink.Unified(patch.getFileName(), patch.getKey(), row - 1,
- PatchTable.this);
- }
- if (patch.getSourceFileName() != null) {
- final String text;
- if (patch.getChangeType() == Patch.ChangeType.RENAMED) {
- text = Util.M.renamedFrom(patch.getSourceFileName());
- } else if (patch.getChangeType() == Patch.ChangeType.COPIED) {
- text = Util.M.copiedFrom(patch.getSourceFileName());
- } else {
- text = Util.M.otherFrom(patch.getSourceFileName());
- }
- final Label line = new Label(text);
- line.setStyleName("SourceFilePath");
- final FlowPanel cell = new FlowPanel();
- cell.add(nameCol);
- cell.add(line);
- nameCol = cell;
- }
- table.setWidget(row, C_PATH, nameCol);
-
- int C_UNIFIED = C_SIDEBYSIDE + 1;
- if (patch.getPatchType() == Patch.PatchType.UNIFIED) {
- table.setWidget(row, C_SIDEBYSIDE,
- new PatchLink.SideBySide(Util.C.patchTableDiffSideBySide(), patch.getKey(), row - 1,
- PatchTable.this));
-
- } else if (patch.getPatchType() == Patch.PatchType.BINARY) {
- C_UNIFIED = C_SIDEBYSIDE + 2;
- }
- table.setWidget(row, C_UNIFIED,
- new PatchLink.Unified(Util.C.patchTableDiffUnified(), patch.getKey(), row - 1,
- PatchTable.this));
- }
-
- void appendHeader(final SafeHtmlBuilder m) {
- m.openTr();
-
- // Cursor
- m.openTd();
- m.addStyleName(S_ICON_HEADER);
- m.addStyleName("LeftMostCell");
- m.nbsp();
- m.closeTd();
-
- // Mode
- m.openTd();
- m.setStyleName(S_ICON_HEADER);
- m.nbsp();
- m.closeTd();
-
- // "File path"
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.append(Util.C.patchTableColumnName());
- m.closeTd();
-
- // "Comments"
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.append(Util.C.patchTableColumnComments());
- m.closeTd();
-
- // "Diff"
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.setAttribute("colspan", 3);
- m.append(Util.C.patchTableColumnDiff());
- m.closeTd();
-
- // "Reviewed"
- if (Gerrit.isSignedIn()) {
- m.openTd();
- m.setStyleName(S_ICON_HEADER);
- m.append(Util.C.reviewed());
- m.closeTd();
- }
-
- m.closeTr();
- }
-
- void appendRow(final SafeHtmlBuilder m, final Patch p) {
- m.openTr();
-
- m.openTd();
- m.addStyleName(S_ICON_CELL);
- m.addStyleName("LeftMostCell");
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName("ChangeTypeCell");
- m.append(p.getChangeType().getCode());
- m.closeTd();
-
- m.openTd();
- m.addStyleName(S_DATA_CELL);
- m.addStyleName("FilePathCell");
- m.closeTd();
-
- m.openTd();
- m.addStyleName(S_DATA_CELL);
- m.addStyleName("CommentCell");
- appendCommentCount(m, p);
- m.closeTd();
-
- switch (p.getPatchType()) {
- case UNIFIED:
- openlink(m, 2);
- m.closeTd();
- break;
-
- case BINARY: {
- String base = GWT.getHostPageBaseURL();
- base += "cat/" + KeyUtil.encode(p.getKey().toString());
- switch (p.getChangeType()) {
- case DELETED:
- case MODIFIED:
- openlink(m, 1);
- m.openAnchor();
- m.setAttribute("href", base + "^1");
- m.append(Util.C.patchTableDownloadPreImage());
- closelink(m);
- break;
- default:
- emptycell(m, 1);
- break;
- }
- switch (p.getChangeType()) {
- case MODIFIED:
- case ADDED:
- openlink(m, 1);
- m.openAnchor();
- m.setAttribute("href", base + "^0");
- m.append(Util.C.patchTableDownloadPostImage());
- closelink(m);
- break;
- default:
- emptycell(m, 1);
- break;
- }
- break;
- }
-
- default:
- emptycell(m, 2);
- break;
- }
-
- openlink(m, 1);
- m.closeTd();
-
- // Green check mark if the user is logged in and they reviewed that file
- if (Gerrit.isSignedIn()) {
- m.openTd();
- m.setStyleName(S_DATA_CELL);
- if (p.isReviewedByCurrentUser()) {
- m.append(SafeHtml.asis(Gerrit.ICONS.greenCheck().getHTML()));
- }
- m.closeTd();
- }
-
- m.closeTr();
- }
-
- void appendCommentCount(final SafeHtmlBuilder m, final Patch p) {
- if (p.getCommentCount() > 0) {
- m.append(Util.M.patchTableComments(p.getCommentCount()));
- }
- if (p.getDraftCount() > 0) {
- if (p.getCommentCount() > 0) {
- m.append(", ");
- }
- m.openSpan();
- m.setStyleName("Drafts");
- m.append(Util.M.patchTableDrafts(p.getDraftCount()));
- m.closeSpan();
- }
- }
-
- private void openlink(final SafeHtmlBuilder m, final int colspan) {
- m.openTd();
- m.addStyleName(S_DATA_CELL);
- m.addStyleName("DiffLinkCell");
- m.setAttribute("colspan", colspan);
- }
-
- private void closelink(final SafeHtmlBuilder m) {
- m.closeAnchor();
- m.closeTd();
- }
-
- private void emptycell(final SafeHtmlBuilder m, final int colspan) {
- m.openTd();
- m.addStyleName(S_DATA_CELL);
- m.addStyleName("DiffLinkCell");
- m.setAttribute("colspan", colspan);
- m.nbsp();
- m.closeTd();
- }
-
- @Override
- protected Object getRowItemKey(final Patch item) {
- return item.getKey();
- }
-
- @Override
- protected void onOpenRow(final int row) {
- Widget link = table.getWidget(row, C_PATH);
- if (link instanceof FlowPanel) {
- link = ((FlowPanel) link).getWidget(0);
- }
- if (link instanceof DirectScreenLink) {
- ((DirectScreenLink) link).go();
- }
- }
- }
-
- private final class DisplayCommand implements IncrementalCommand {
- private final MyTable table;
- private final List<Patch> list;
- private boolean attached;
- private SafeHtmlBuilder nc = new SafeHtmlBuilder();
- private int stage = 0;
- private int row;
- private double start;
- private ProgressBar meter;
-
- private DisplayCommand(final List<Patch> list) {
- this.table = new MyTable();
- this.list = list;
- }
-
- /**
- * Add the files contained in the list of patches to the table, one per row.
- */
- @SuppressWarnings("fallthrough")
- public boolean execute() {
- final boolean attachedNow = isAttached();
- if (!attached && attachedNow) {
- // Remember that we have been attached at least once. If
- // later we find we aren't attached we should stop running.
- //
- attached = true;
- } else if (attached && !attachedNow) {
- // If the user navigated away, we aren't in the DOM anymore.
- // Don't continue to render.
- //
- return false;
- }
-
- start = System.currentTimeMillis();
- switch (stage) {
- case 0:
- if (row == 0) {
- table.appendHeader(nc);
- }
- while (row < list.size()) {
- table.appendRow(nc, list.get(row));
- if ((++row % 10) == 0 && longRunning()) {
- updateMeter();
- return true;
- }
- }
- table.resetHtml(nc);
- nc = null;
- stage = 1;
- row = 0;
-
- case 1:
- while (row < list.size()) {
- table.initializeRow(row + 1);
- if ((++row % 50) == 0 && longRunning()) {
- updateMeter();
- return true;
- }
- }
- updateMeter();
- showTable();
- }
- return false;
- }
-
- void showTable() {
- PatchTable.this.myBody.clear();
- PatchTable.this.myBody.add(table);
- PatchTable.this.myTable = table;
- table.finishDisplay();
- if (PatchTable.this.onLoadCommand != null) {
- PatchTable.this.onLoadCommand.execute();
- PatchTable.this.onLoadCommand = null;
- }
- }
-
- void initMeter() {
- if (meter == null) {
- meter = new ProgressBar(Util.M.loadingPatchSet(psid.get()));
- PatchTable.this.myBody.clear();
- PatchTable.this.myBody.add(meter);
- }
- updateMeter();
- }
-
- void updateMeter() {
- if (meter != null) {
- final int n = list.size();
- meter.setValue(((100 * (stage * n + row)) / (2 * n)));
- }
- }
-
- private boolean longRunning() {
- return System.currentTimeMillis() - start > 200;
- }
- }
-
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
deleted file mode 100644
index bac997a612..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
+++ /dev/null
@@ -1,325 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.patches.CommentEditorPanel;
-import com.google.gerrit.client.patches.PatchUtil;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
-import com.google.gerrit.client.ui.AccountScreen;
-import com.google.gerrit.client.ui.PatchLink;
-import com.google.gerrit.client.ui.SmallHeading;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.FormPanel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwt.user.client.ui.VerticalPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.FormPanel.SubmitEvent;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class PublishCommentScreen extends AccountScreen implements ClickHandler {
- private static SavedState lastState;
-
- private final PatchSet.Id patchSetId;
- private Collection<ValueRadioButton> approvalButtons;
- private ChangeDescriptionBlock descBlock;
- private Panel approvalPanel;
- private NpTextArea message;
- private Panel draftsPanel;
- private Button send;
- private Button cancel;
- private boolean saveStateOnUnload = true;
- private List<CommentEditorPanel> commentEditors;
-
- public PublishCommentScreen(final PatchSet.Id psi) {
- patchSetId = psi;
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
- addStyleName("gerrit-PublishCommentsScreen");
-
- approvalButtons = new ArrayList<ValueRadioButton>();
- descBlock = new ChangeDescriptionBlock();
- add(descBlock);
-
- final FormPanel form = new FormPanel();
- final FlowPanel body = new FlowPanel();
- form.setWidget(body);
- form.addSubmitHandler(new FormPanel.SubmitHandler() {
- @Override
- public void onSubmit(final SubmitEvent event) {
- event.cancel();
- }
- });
- add(form);
-
- approvalPanel = new FlowPanel();
- body.add(approvalPanel);
- initMessage(body);
-
- draftsPanel = new FlowPanel();
- body.add(draftsPanel);
-
- final FlowPanel buttonRow = new FlowPanel();
- buttonRow.setStyleName("gerrit-CommentEditor-Buttons");
- body.add(buttonRow);
-
- send = new Button(Util.C.buttonPublishCommentsSend());
- send.addClickHandler(this);
- buttonRow.add(send);
-
- cancel = new Button(Util.C.buttonPublishCommentsCancel());
- cancel.addClickHandler(this);
- buttonRow.add(cancel);
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- Util.DETAIL_SVC.patchSetPublishDetail(patchSetId,
- new ScreenLoadCallback<PatchSetPublishDetail>(this) {
- @Override
- protected void preDisplay(final PatchSetPublishDetail result) {
- send.setEnabled(true);
- display(result);
- }
-
- @Override
- protected void postDisplay() {
- message.setFocus(true);
- }
- });
- }
-
- @Override
- protected void onUnload() {
- super.onUnload();
- lastState = saveStateOnUnload ? new SavedState(this) : null;
- }
-
- @Override
- public void onClick(final ClickEvent event) {
- final Widget sender = (Widget) event.getSource();
- if (send == sender) {
- onSend();
- } else if (cancel == sender) {
- saveStateOnUnload = false;
- goChange();
- }
- }
-
- private void initMessage(final Panel body) {
- body.add(new SmallHeading(Util.C.headingCoverMessage()));
-
- final VerticalPanel mwrap = new VerticalPanel();
- mwrap.setStyleName("gerrit-CoverMessage");
- body.add(mwrap);
-
- message = new NpTextArea();
- message.setCharacterWidth(60);
- message.setVisibleLines(10);
- DOM.setElementPropertyBoolean(message.getElement(), "spellcheck", true);
- mwrap.add(message);
- }
-
- private void initApprovals(final PatchSetPublishDetail r, final Panel body) {
- for (final ApprovalType ct : Gerrit.getConfig().getApprovalTypes()
- .getApprovalTypes()) {
- if (r.isAllowed(ct.getCategory().getId())) {
- initApprovalType(r, body, ct);
- }
- }
- }
-
- private void initApprovalType(final PatchSetPublishDetail r,
- final Panel body, final ApprovalType ct) {
- body.add(new SmallHeading(ct.getCategory().getName() + ":"));
-
- final VerticalPanel vp = new VerticalPanel();
- vp.setStyleName("gerrit-ApprovalCategoryList");
- final List<ApprovalCategoryValue> lst =
- new ArrayList<ApprovalCategoryValue>(ct.getValues());
- Collections.reverse(lst);
- final ApprovalCategory.Id catId = ct.getCategory().getId();
- final Set<ApprovalCategoryValue.Id> allowed = r.getAllowed(catId);
- final PatchSetApproval prior = r.getChangeApproval(catId);
-
- for (final ApprovalCategoryValue buttonValue : lst) {
- if (!allowed.contains(buttonValue.getId())) {
- continue;
- }
-
- final ValueRadioButton b =
- new ValueRadioButton(buttonValue, ct.getCategory().getName());
- b.setText(buttonValue.format());
-
- if (lastState != null && patchSetId.equals(lastState.patchSetId)
- && lastState.approvals.containsKey(buttonValue.getCategoryId())) {
- b.setValue(lastState.approvals.get(buttonValue.getCategoryId()).equals(
- buttonValue));
- } else {
- b.setValue(prior != null ? buttonValue.getValue() == prior.getValue()
- : buttonValue.getValue() == 0);
- }
-
- approvalButtons.add(b);
- vp.add(b);
- }
- body.add(vp);
- }
-
- private void display(final PatchSetPublishDetail r) {
- setPageTitle(Util.M.publishComments(r.getChange().getKey().abbreviate(),
- patchSetId.get()));
- descBlock.display(r.getChange(), r.getPatchSetInfo(), r.getAccounts());
-
- if (r.getChange().getStatus().isOpen()) {
- initApprovals(r, approvalPanel);
- }
- if (lastState != null && patchSetId.equals(lastState.patchSetId)) {
- message.setText(lastState.message);
- }
-
- draftsPanel.clear();
- commentEditors = new ArrayList<CommentEditorPanel>();
-
- if (!r.getDrafts().isEmpty()) {
- draftsPanel.add(new SmallHeading(Util.C.headingPatchComments()));
-
- Panel panel = null;
- String priorFile = "";
- for (final PatchLineComment c : r.getDrafts()) {
- final Patch.Key patchKey = c.getKey().getParentKey();
- final String fn = patchKey.get();
- if (!fn.equals(priorFile)) {
- panel = new FlowPanel();
- panel.addStyleName("gerrit-PatchComments");
- draftsPanel.add(panel);
- // Parent table can be null here since we are not showing any
- // next/previous links
- panel.add(new PatchLink.SideBySide(fn, patchKey, 0, null /*
- * parent
- * table
- */));
- priorFile = fn;
- }
-
- final CommentEditorPanel editor = new CommentEditorPanel(c);
- editor.setAuthorNameText(Util.M.lineHeader(c.getLine()));
- editor.setOpen(true);
- commentEditors.add(editor);
- panel.add(editor);
- }
- }
- }
-
- private void onSend() {
- if (commentEditors.isEmpty()) {
- onSend2();
- } else {
- final GerritCallback<VoidResult> afterSaveDraft =
- new GerritCallback<VoidResult>() {
- private int done;
-
- @Override
- public void onSuccess(final VoidResult result) {
- if (++done == commentEditors.size()) {
- onSend2();
- }
- }
- };
- for (final CommentEditorPanel p : commentEditors) {
- p.saveDraft(afterSaveDraft);
- }
- }
- }
-
- private void onSend2() {
- final Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> values =
- new HashMap<ApprovalCategory.Id, ApprovalCategoryValue.Id>();
- for (final ValueRadioButton b : approvalButtons) {
- if (b.getValue()) {
- values.put(b.value.getCategoryId(), b.value.getId());
- }
- }
-
- PatchUtil.DETAIL_SVC.publishComments(patchSetId, message.getText().trim(),
- new HashSet<ApprovalCategoryValue.Id>(values.values()),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- saveStateOnUnload = false;
- goChange();
- }
- });
- }
-
- private void goChange() {
- final Change.Id ck = patchSetId.getParentKey();
- Gerrit.display(Link.toChange(ck), new ChangeScreen(ck));
- }
-
- private static class ValueRadioButton extends RadioButton {
- final ApprovalCategoryValue value;
-
- ValueRadioButton(final ApprovalCategoryValue v, final String label) {
- super(label);
- value = v;
- }
- }
-
- private static class SavedState {
- final PatchSet.Id patchSetId;
- final String message;
- final Map<ApprovalCategory.Id, ApprovalCategoryValue> approvals;
-
- SavedState(final PublishCommentScreen p) {
- patchSetId = p.patchSetId;
- message = p.message.getText();
- approvals = new HashMap<ApprovalCategory.Id, ApprovalCategoryValue>();
- for (final ValueRadioButton b : p.approvalButtons) {
- if (b.getValue()) {
- approvals.put(b.value.getCategoryId(), b.value);
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java b/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java
deleted file mode 100644
index e331550f64..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/SubmitFailureDialog.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtexpui.user.client.AutoCenterDialogBox;
-
-class SubmitFailureDialog extends AutoCenterDialogBox {
- SubmitFailureDialog(final ChangeDetail result, final ChangeMessage msg) {
- setText(Util.C.submitFailed());
-
- final FlowPanel body = new FlowPanel();
- final Widget msgText =
- new SafeHtmlBuilder().append(msg.getMessage().trim()).wikify()
- .toBlockWidget();
- body.add(msgText);
-
- final FlowPanel buttonPanel = new FlowPanel();
- buttonPanel.setStyleName("gerrit-CommentEditor-Buttons");
- Button close = new Button(Util.C.buttonClose());
- close.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- hide();
- }
- });
- buttonPanel.add(close);
- body.add(buttonPanel);
-
- add(body);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/ToggleStarRequest.java b/src/main/java/com/google/gerrit/client/changes/ToggleStarRequest.java
deleted file mode 100644
index b8ce1fe4af..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/ToggleStarRequest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.reviewdb.Change;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** Request parameters to update the changes the user has toggled. */
-public class ToggleStarRequest {
- protected Set<Change.Id> add;
- protected Set<Change.Id> remove;
-
- /**
- * Request an update to the change's star status.
- *
- * @param id unique id of the change, must not be null.
- * @param on true if the change should now be starred; false if it should now
- * be not starred.
- */
- public void toggle(final Change.Id id, final boolean on) {
- if (on) {
- if (add == null) {
- add = new HashSet<Change.Id>();
- }
- add.add(id);
- if (remove != null) {
- remove.remove(id);
- }
- } else {
- if (remove == null) {
- remove = new HashSet<Change.Id>();
- }
- remove.add(id);
- if (add != null) {
- add.remove(id);
- }
- }
- }
-
- /** Get the set of changes which should have stars added; may be null. */
- public Set<Change.Id> getAddSet() {
- return add;
- }
-
- /** Get the set of changes which should have stars removed; may be null. */
- public Set<Change.Id> getRemoveSet() {
- return remove;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/changes/Util.java b/src/main/java/com/google/gerrit/client/changes/Util.java
deleted file mode 100644
index 9461edb4f7..0000000000
--- a/src/main/java/com/google/gerrit/client/changes/Util.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.changes;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class Util {
- public static final ChangeConstants C = GWT.create(ChangeConstants.class);
- public static final ChangeMessages M = GWT.create(ChangeMessages.class);
-
- public static final ChangeDetailService DETAIL_SVC;
- public static final ChangeListService LIST_SVC;
- public static final ChangeManageService MANAGE_SVC;
-
- static {
- DETAIL_SVC = GWT.create(ChangeDetailService.class);
- JsonUtil.bind(DETAIL_SVC, "rpc/ChangeDetailService");
-
- LIST_SVC = GWT.create(ChangeListService.class);
- JsonUtil.bind(LIST_SVC, "rpc/ChangeListService");
-
- MANAGE_SVC = GWT.create(ChangeManageService.class);
- JsonUtil.bind(MANAGE_SVC, "rpc/ChangeManageService");
- }
-
- public static String toLongString(final Change.Status status) {
- if (status == null) {
- return "";
- }
- switch (status) {
- case NEW:
- return C.statusLongNew();
- case SUBMITTED:
- return C.statusLongSubmitted();
- case MERGED:
- return C.statusLongMerged();
- case ABANDONED:
- return C.statusLongAbandoned();
- default:
- return status.name();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/AccountDashboardInfo.java b/src/main/java/com/google/gerrit/client/data/AccountDashboardInfo.java
deleted file mode 100644
index 23d1620e51..0000000000
--- a/src/main/java/com/google/gerrit/client/data/AccountDashboardInfo.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.changes.AccountDashboardScreen;
-import com.google.gerrit.client.reviewdb.Account;
-
-import java.util.List;
-
-/** Summary information needed for {@link AccountDashboardScreen}. */
-public class AccountDashboardInfo {
- protected AccountInfoCache accounts;
- protected Account.Id owner;
- protected List<ChangeInfo> byOwner;
- protected List<ChangeInfo> forReview;
- protected List<ChangeInfo> closed;
-
- protected AccountDashboardInfo() {
- }
-
- public AccountDashboardInfo(final Account.Id forUser) {
- owner = forUser;
- }
-
- public AccountInfoCache getAccounts() {
- return accounts;
- }
-
- public void setAccounts(final AccountInfoCache ac) {
- accounts = ac;
- }
-
- public Account.Id getOwner() {
- return owner;
- }
-
- public List<ChangeInfo> getByOwner() {
- return byOwner;
- }
-
- public void setByOwner(List<ChangeInfo> c) {
- byOwner = c;
- }
-
- public List<ChangeInfo> getForReview() {
- return forReview;
- }
-
- public void setForReview(List<ChangeInfo> c) {
- forReview = c;
- }
-
- public List<ChangeInfo> getClosed() {
- return closed;
- }
-
- public void setClosed(List<ChangeInfo> c) {
- closed = c;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/AccountInfo.java b/src/main/java/com/google/gerrit/client/data/AccountInfo.java
deleted file mode 100644
index aa6b8f9b79..0000000000
--- a/src/main/java/com/google/gerrit/client/data/AccountInfo.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Summary information about an {@link Account}, for simple tabular displays. */
-public class AccountInfo {
- protected Account.Id id;
- protected String fullName;
- protected String preferredEmail;
-
- protected AccountInfo() {
- }
-
- /**
- * Create an 'Anonymous Coward' account info, when only the id is known.
- * <p>
- * This constructor should only be a last-ditch effort, when the usual account
- * lookup has failed and a stale account id has been discovered in the data
- * store.
- */
- public AccountInfo(final Account.Id id) {
- this.id = id;
- }
-
- /**
- * Create an account description from a real data store record.
- *
- * @param a the data store record holding the specific account details.
- */
- public AccountInfo(final Account a) {
- id = a.getId();
- fullName = a.getFullName();
- preferredEmail = a.getPreferredEmail();
- }
-
- /** @return the unique local id of the account */
- public Account.Id getId() {
- return id;
- }
-
- /** @return the full name of the account holder; null if not supplied */
- public String getFullName() {
- return fullName;
- }
-
- /** @return the email address of the account holder; null if not supplied */
- public String getPreferredEmail() {
- return preferredEmail;
- }
-
- public void setPreferredEmail(final String email) {
- preferredEmail = email;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/AccountInfoCache.java b/src/main/java/com/google/gerrit/client/data/AccountInfoCache.java
deleted file mode 100644
index 678ed766ab..0000000000
--- a/src/main/java/com/google/gerrit/client/data/AccountInfoCache.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/** In-memory table of {@link AccountInfo}, indexed by {@link Account.Id}. */
-public class AccountInfoCache {
- private static final AccountInfoCache EMPTY;
- static {
- EMPTY = new AccountInfoCache();
- EMPTY.accounts = Collections.emptyMap();
- }
-
- /** Obtain an empty cache singleton. */
- public static AccountInfoCache empty() {
- return EMPTY;
- }
-
- protected Map<Account.Id, AccountInfo> accounts;
-
- protected AccountInfoCache() {
- }
-
- public AccountInfoCache(final Iterable<AccountInfo> list) {
- accounts = new HashMap<Account.Id, AccountInfo>();
- for (final AccountInfo ai : list) {
- accounts.put(ai.getId(), ai);
- }
- }
-
- /**
- * Lookup the account summary
- * <p>
- * The return value can take on one of three forms:
- * <ul>
- * <li><code>null</code>, if <code>id == null</code>.</li>
- * <li>a valid info block, if <code>id</code> was loaded.</li>
- * <li>an anonymous info block, if <code>id</code> was not loaded.</li>
- * </ul>
- *
- * @param id the id desired.
- * @return info block for the account.
- */
- public AccountInfo get(final Account.Id id) {
- if (id == null) {
- return null;
- }
-
- AccountInfo r = accounts.get(id);
- if (r == null) {
- r = new AccountInfo(id);
- accounts.put(id, r);
- }
- return r;
- }
-
- /** Merge the information from another cache into this one. */
- public void merge(final AccountInfoCache other) {
- assert this != EMPTY;
- accounts.putAll(other.accounts);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ApprovalDetail.java b/src/main/java/com/google/gerrit/client/data/ApprovalDetail.java
deleted file mode 100644
index 1a482b2812..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ApprovalDetail.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ApprovalDetail {
- public static final Comparator<ApprovalDetail> SORT =
- new Comparator<ApprovalDetail>() {
- public int compare(final ApprovalDetail o1, final ApprovalDetail o2) {
- int cmp;
- cmp = o2.hasNonZero - o1.hasNonZero;
- if (cmp != 0) return cmp;
- return o1.sortOrder.compareTo(o2.sortOrder);
- }
- };
-
- static final Timestamp EG_0 = new Timestamp(0);
- static final Timestamp EG_D = new Timestamp(Long.MAX_VALUE);
-
- protected Account.Id account;
- protected List<PatchSetApproval> approvals;
-
- private transient int hasNonZero;
- private transient Timestamp sortOrder = EG_D;
-
- protected ApprovalDetail() {
- }
-
- public ApprovalDetail(final Account.Id id) {
- account = id;
- approvals = new ArrayList<PatchSetApproval>();
- }
-
- public Account.Id getAccount() {
- return account;
- }
-
- public Map<ApprovalCategory.Id, PatchSetApproval> getApprovalMap() {
- final HashMap<ApprovalCategory.Id, PatchSetApproval> r;
- r = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- for (final PatchSetApproval ca : approvals) {
- r.put(ca.getCategoryId(), ca);
- }
- return r;
- }
-
- public void sortFirst() {
- hasNonZero = 1;
- sortOrder = ApprovalDetail.EG_0;
- }
-
- public void add(final PatchSetApproval ca) {
- approvals.add(ca);
-
- final Timestamp g = ca.getGranted();
- if (g != null && g.compareTo(sortOrder) < 0) {
- sortOrder = g;
- }
- if (ca.getValue() != 0) {
- hasNonZero = 1;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ApprovalSummary.java b/src/main/java/com/google/gerrit/client/data/ApprovalSummary.java
deleted file mode 100644
index 39df89ff9f..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ApprovalSummary.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Summarizes the approvals (or negative approvals) for a patch set.
- * This will typically contain zero or one approvals for each
- * category, with all of the approvals coming from a single patch set.
- */
-public class ApprovalSummary {
- protected Map<ApprovalCategory.Id, PatchSetApproval> approvals;
-
- protected ApprovalSummary() {
- }
-
- public ApprovalSummary(final Iterable<PatchSetApproval> list) {
- approvals = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- for (final PatchSetApproval a : list) {
- approvals.put(a.getCategoryId(), a);
- }
- }
-
- public Map<ApprovalCategory.Id, PatchSetApproval> getApprovalMap() {
- return Collections.unmodifiableMap(approvals);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ApprovalSummarySet.java b/src/main/java/com/google/gerrit/client/data/ApprovalSummarySet.java
deleted file mode 100644
index 4478476c59..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ApprovalSummarySet.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Change;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/** Contains a set of ApprovalSummary objects, keyed by the change id
- * from which they were derived.
- */
-public class ApprovalSummarySet {
- protected AccountInfoCache accounts;
-
- protected Map<Change.Id, ApprovalSummary> summaries;
-
- protected ApprovalSummarySet() {
- }
-
- public ApprovalSummarySet(final AccountInfoCache accts,
- final Map<Change.Id, ApprovalSummary> map) {
- accounts = accts;
-
- summaries = new HashMap<Change.Id, ApprovalSummary>();
- summaries.putAll(map);
- }
-
- public AccountInfoCache getAccountInfoCache() {
- return accounts;
- }
-
- public Map<Change.Id, ApprovalSummary> getSummaryMap() {
- return Collections.unmodifiableMap(summaries);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ApprovalType.java b/src/main/java/com/google/gerrit/client/data/ApprovalType.java
deleted file mode 100644
index 2437969c44..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ApprovalType.java
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ApprovalType {
- protected ApprovalCategory category;
- protected List<ApprovalCategoryValue> values;
- protected short maxNegative;
- protected short maxPositive;
-
- private transient Map<Short, ApprovalCategoryValue> byValue;
-
- protected ApprovalType() {
- }
-
- public ApprovalType(final ApprovalCategory ac,
- final List<ApprovalCategoryValue> valueList) {
- category = ac;
- values = new ArrayList<ApprovalCategoryValue>(valueList);
- Collections.sort(values, new Comparator<ApprovalCategoryValue>() {
- public int compare(ApprovalCategoryValue o1, ApprovalCategoryValue o2) {
- return o1.getValue() - o2.getValue();
- }
- });
-
- maxNegative = Short.MIN_VALUE;
- maxPositive = Short.MAX_VALUE;
- if (values.size() > 0) {
- if (values.get(0).getValue() < 0) {
- maxNegative = values.get(0).getValue();
- }
- if (values.get(values.size() - 1).getValue() > 0) {
- maxPositive = values.get(values.size() - 1).getValue();
- }
- }
- }
-
- public ApprovalCategory getCategory() {
- return category;
- }
-
- public List<ApprovalCategoryValue> getValues() {
- return values;
- }
-
- public ApprovalCategoryValue getMin() {
- if (values.isEmpty()) {
- return null;
- }
- return values.get(0);
- }
-
- public ApprovalCategoryValue getMax() {
- if (values.isEmpty()) {
- return null;
- }
- final ApprovalCategoryValue v = values.get(values.size() - 1);
- return v.getValue() > 0 ? v : null;
- }
-
- public boolean isMaxNegative(final PatchSetApproval ca) {
- return maxNegative == ca.getValue();
- }
-
- public boolean isMaxPositive(final PatchSetApproval ca) {
- return maxPositive == ca.getValue();
- }
-
- public ApprovalCategoryValue getValue(final short value) {
- initByValue();
- return byValue.get(value);
- }
-
- public ApprovalCategoryValue getValue(final PatchSetApproval ca) {
- initByValue();
- return byValue.get(ca.getValue());
- }
-
- private void initByValue() {
- if (byValue == null) {
- byValue = new HashMap<Short, ApprovalCategoryValue>();
- for (final ApprovalCategoryValue acv : values) {
- byValue.put(acv.getValue(), acv);
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ApprovalTypes.java b/src/main/java/com/google/gerrit/client/data/ApprovalTypes.java
deleted file mode 100644
index 9d91ef870a..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ApprovalTypes.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class ApprovalTypes {
- protected List<ApprovalType> approvalTypes;
- protected List<ApprovalType> actionTypes;
- private transient Map<ApprovalCategory.Id, ApprovalType> byCategoryId;
-
- protected ApprovalTypes() {
- }
-
- public ApprovalTypes(final List<ApprovalType> approvals,
- final List<ApprovalType> actions) {
- approvalTypes = approvals;
- actionTypes = actions;
- byCategory();
- }
-
- public List<ApprovalType> getApprovalTypes() {
- return approvalTypes;
- }
-
- public List<ApprovalType> getActionTypes() {
- return actionTypes;
- }
-
- public ApprovalType getApprovalType(final ApprovalCategory.Id id) {
- return byCategory().get(id);
- }
-
- private Map<ApprovalCategory.Id, ApprovalType> byCategory() {
- if (byCategoryId == null) {
- byCategoryId = new HashMap<ApprovalCategory.Id, ApprovalType>();
- if (actionTypes != null) {
- for (final ApprovalType t : actionTypes) {
- byCategoryId.put(t.getCategory().getId(), t);
- }
- }
-
- if (approvalTypes != null) {
- for (final ApprovalType t : approvalTypes) {
- byCategoryId.put(t.getCategory().getId(), t);
- }
- }
- }
- return byCategoryId;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ChangeDetail.java b/src/main/java/com/google/gerrit/client/data/ChangeDetail.java
deleted file mode 100644
index 8aa32988bc..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ChangeDetail.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.changes.ChangeScreen;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/** Detail necessary to display {@link ChangeScreen}. */
-public class ChangeDetail {
- protected AccountInfoCache accounts;
- protected boolean allowsAnonymous;
- protected boolean canAbandon;
- protected Change change;
- protected boolean starred;
- protected List<ChangeInfo> dependsOn;
- protected List<ChangeInfo> neededBy;
- protected List<PatchSet> patchSets;
- protected List<ApprovalDetail> approvals;
- protected Set<ApprovalCategory.Id> missingApprovals;
- protected List<ChangeMessage> messages;
- protected PatchSet.Id currentPatchSetId;
- protected PatchSetDetail currentDetail;
- protected Set<ApprovalCategory.Id> currentActions;
-
- public ChangeDetail() {
- }
-
- public AccountInfoCache getAccounts() {
- return accounts;
- }
-
- public void setAccounts(AccountInfoCache aic) {
- accounts = aic;
- }
-
- public boolean isAllowsAnonymous() {
- return allowsAnonymous;
- }
-
- public void setAllowsAnonymous(final boolean anon) {
- allowsAnonymous = anon;
- }
-
- public boolean canAbandon() {
- return canAbandon;
- }
-
- public void setCanAbandon(final boolean a) {
- canAbandon = a;
- }
-
- public Change getChange() {
- return change;
- }
-
- public void setChange(final Change change) {
- this.change = change;
- this.currentPatchSetId = change.currentPatchSetId();
- }
-
- public boolean isStarred() {
- return starred;
- }
-
- public void setStarred(final boolean s) {
- starred = s;
- }
-
- public List<ChangeInfo> getDependsOn() {
- return dependsOn;
- }
-
- public void setDependsOn(List<ChangeInfo> d) {
- dependsOn = d;
- }
-
- public List<ChangeInfo> getNeededBy() {
- return neededBy;
- }
-
- public void setNeededBy(List<ChangeInfo> d) {
- neededBy = d;
- }
-
- public List<ChangeMessage> getMessages() {
- return messages;
- }
-
- public void setMessages(List<ChangeMessage> m) {
- messages = m;
- }
-
- public List<PatchSet> getPatchSets() {
- return patchSets;
- }
-
- public void setPatchSets(List<PatchSet> s) {
- patchSets = s;
- }
-
- public List<ApprovalDetail> getApprovals() {
- return approvals;
- }
-
- public void setApprovals(Collection<ApprovalDetail> list) {
- approvals = new ArrayList<ApprovalDetail>(list);
- Collections.sort(approvals, ApprovalDetail.SORT);
- }
-
- public Set<ApprovalCategory.Id> getMissingApprovals() {
- return missingApprovals;
- }
-
- public void setMissingApprovals(Set<ApprovalCategory.Id> a) {
- missingApprovals = a;
- }
-
- public Set<ApprovalCategory.Id> getCurrentActions() {
- return currentActions;
- }
-
- public void setCurrentActions(Set<ApprovalCategory.Id> a) {
- currentActions = a;
- }
-
- public boolean isCurrentPatchSet(final PatchSetDetail detail) {
- return currentPatchSetId != null
- && detail.getPatchSet().getId().equals(currentPatchSetId);
- }
-
- public PatchSet getCurrentPatchSet() {
- if (currentPatchSetId != null) {
- // We search through the list backwards because its *very* likely
- // that the current patch set is also the last patch set.
- //
- for (int i = patchSets.size() - 1; i >= 0; i--) {
- final PatchSet ps = patchSets.get(i);
- if (ps.getId().equals(currentPatchSetId)) {
- return ps;
- }
- }
- }
- return null;
- }
-
- public PatchSetDetail getCurrentPatchSetDetail() {
- return currentDetail;
- }
-
- public void setCurrentPatchSetDetail(PatchSetDetail d) {
- currentDetail = d;
- }
-
- public String getDescription() {
- return currentDetail != null ? currentDetail.getInfo().getMessage() : "";
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ChangeInfo.java b/src/main/java/com/google/gerrit/client/data/ChangeInfo.java
deleted file mode 100644
index 4aa9879a32..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ChangeInfo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-
-import java.sql.Timestamp;
-
-public class ChangeInfo {
- protected Change.Id id;
- protected Change.Key key;
- protected Account.Id owner;
- protected String subject;
- protected Change.Status status;
- protected ProjectInfo project;
- protected String branch;
- protected boolean starred;
- protected Timestamp lastUpdatedOn;
- protected String sortKey;
-
- protected ChangeInfo() {
- }
-
- public ChangeInfo(final Change c) {
- id = c.getId();
- key = c.getKey();
- owner = c.getOwner();
- subject = c.getSubject();
- status = c.getStatus();
- project = new ProjectInfo(c.getProject());
- branch = c.getDest().getShortName();
- lastUpdatedOn = c.getLastUpdatedOn();
- sortKey = c.getSortKey();
- }
-
- public Change.Id getId() {
- return id;
- }
-
- public Change.Key getKey() {
- return key;
- }
-
- public Account.Id getOwner() {
- return owner;
- }
-
- public String getSubject() {
- return subject;
- }
-
- public Change.Status getStatus() {
- return status;
- }
-
- public ProjectInfo getProject() {
- return project;
- }
-
- public String getBranch() {
- return branch;
- }
-
- public boolean isStarred() {
- return starred;
- }
-
- public void setStarred(final boolean s) {
- starred = s;
- }
-
- public java.sql.Timestamp getLastUpdatedOn() {
- return lastUpdatedOn;
- }
-
- public String getSortKey() {
- return sortKey;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/EditList.java b/src/main/java/com/google/gerrit/client/data/EditList.java
deleted file mode 100644
index 019eaf0b06..0000000000
--- a/src/main/java/com/google/gerrit/client/data/EditList.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import org.eclipse.jgit.diff.Edit;
-
-import java.util.Iterator;
-import java.util.List;
-
-public class EditList {
- private final List<Edit> edits;
- private final int context;
- private final int aSize;
- private final int bSize;
-
- public EditList(final List<Edit> edits, final int contextLines,
- final int aSize, final int bSize) {
- this.edits = edits;
- this.context = contextLines;
- this.aSize = aSize;
- this.bSize = bSize;
- }
-
- public List<Edit> getEdits() {
- return edits;
- }
-
- public Iterable<Hunk> getHunks() {
- return new Iterable<Hunk>() {
- public Iterator<Hunk> iterator() {
- return new Iterator<Hunk>() {
- private int curIdx;
-
- public boolean hasNext() {
- return curIdx < edits.size();
- }
-
- public Hunk next() {
- final int c = curIdx;
- final int e = findCombinedEnd(c);
- curIdx = e + 1;
- return new Hunk(c, e);
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
- };
- }
-
- private int findCombinedEnd(final int i) {
- int end = i + 1;
- while (end < edits.size() && (combineA(end) || combineB(end)))
- end++;
- return end - 1;
- }
-
- private boolean combineA(final int i) {
- final Edit s = edits.get(i);
- final Edit e = edits.get(i - 1);
- return s.getBeginA() - e.getEndA() <= 2 * context;
- }
-
- private boolean combineB(final int i) {
- final int s = edits.get(i).getBeginB();
- final int e = edits.get(i - 1).getEndB();
- return s - e <= 2 * context;
- }
-
- public class Hunk {
- private int curIdx;
- private Edit curEdit;
- private final int endIdx;
- private final Edit endEdit;
-
- private int aCur;
- private int bCur;
- private final int aEnd;
- private final int bEnd;
-
- private Hunk(final int ci, final int ei) {
- curIdx = ci;
- endIdx = ei;
- curEdit = edits.get(curIdx);
- endEdit = edits.get(endIdx);
-
- aCur = Math.max(0, curEdit.getBeginA() - context);
- bCur = Math.max(0, curEdit.getBeginB() - context);
- aEnd = Math.min(aSize, endEdit.getEndA() + context);
- bEnd = Math.min(bSize, endEdit.getEndB() + context);
- }
-
- public int getCurA() {
- return aCur;
- }
-
- public int getCurB() {
- return bCur;
- }
-
- public int getEndA() {
- return aEnd;
- }
-
- public int getEndB() {
- return bEnd;
- }
-
- public void incA() {
- aCur++;
- }
-
- public void incB() {
- bCur++;
- }
-
- public void incBoth() {
- incA();
- incB();
- }
-
- public boolean isStartOfFile() {
- return aCur == 0 && bCur == 0;
- }
-
- public boolean isContextLine() {
- return !isModifiedLine();
- }
-
- public boolean isDeletedA() {
- return curEdit.getBeginA() <= aCur && aCur < curEdit.getEndA();
- }
-
- public boolean isInsertedB() {
- return curEdit.getBeginB() <= bCur && bCur < curEdit.getEndB();
- }
-
- public boolean isModifiedLine() {
- return isDeletedA() || isInsertedB();
- }
-
- public boolean next() {
- if (!in(curEdit)) {
- if (curIdx < endIdx) {
- curEdit = edits.get(++curIdx);
- }
- }
- return aCur < aEnd || bCur < bEnd;
- }
-
- private boolean in(final Edit edit) {
- return aCur < edit.getEndA() || bCur < edit.getEndB();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/GerritConfig.java b/src/main/java/com/google/gerrit/client/data/GerritConfig.java
deleted file mode 100644
index 5344a2558d..0000000000
--- a/src/main/java/com/google/gerrit/client/data/GerritConfig.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AuthType;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gwtexpui.safehtml.client.RegexFindReplace;
-
-import java.util.List;
-import java.util.Set;
-
-public class GerritConfig implements Cloneable {
- protected String canonicalUrl;
- protected GitwebLink gitweb;
- protected boolean useContributorAgreements;
- protected boolean useContactInfo;
- protected boolean allowRegisterNewEmail;
- protected AuthType authType;
- protected boolean useRepoDownload;
- protected String gitDaemonUrl;
- protected String sshdAddress;
- protected Project.NameKey wildProject;
- protected ApprovalTypes approvalTypes;
- protected Set<Account.FieldName> editableAccountFields;
- protected List<RegexFindReplace> commentLinks;
-
- public String getCanonicalUrl() {
- return canonicalUrl;
- }
-
- public void setCanonicalUrl(final String u) {
- canonicalUrl = u;
- }
-
- public AuthType getAuthType() {
- return authType;
- }
-
- public void setAuthType(final AuthType t) {
- authType = t;
- }
-
- public GitwebLink getGitwebLink() {
- return gitweb;
- }
-
- public void setGitwebLink(final GitwebLink w) {
- gitweb = w;
- }
-
- public boolean isUseContributorAgreements() {
- return useContributorAgreements;
- }
-
- public void setUseContributorAgreements(final boolean r) {
- useContributorAgreements = r;
- }
-
- public boolean isUseContactInfo() {
- return useContactInfo;
- }
-
- public void setUseContactInfo(final boolean r) {
- useContactInfo = r;
- }
-
- public boolean isUseRepoDownload() {
- return useRepoDownload;
- }
-
- public void setUseRepoDownload(final boolean r) {
- useRepoDownload = r;
- }
-
- public String getGitDaemonUrl() {
- return gitDaemonUrl;
- }
-
- public void setGitDaemonUrl(String url) {
- if (url != null && !url.endsWith("/")) {
- url += "/";
- }
- gitDaemonUrl = url;
- }
-
- public String getSshdAddress() {
- return sshdAddress;
- }
-
- public void setSshdAddress(final String addr) {
- sshdAddress = addr;
- }
-
- public Project.NameKey getWildProject() {
- return wildProject;
- }
-
- public void setWildProject(final Project.NameKey wp) {
- wildProject = wp;
- }
-
- public ApprovalTypes getApprovalTypes() {
- return approvalTypes;
- }
-
- public void setApprovalTypes(final ApprovalTypes at) {
- approvalTypes = at;
- }
-
- public boolean canEdit(final Account.FieldName f) {
- return editableAccountFields.contains(f);
- }
-
- public Set<Account.FieldName> getEditableAccountFields() {
- return editableAccountFields;
- }
-
- public void setEditableAccountFields(final Set<Account.FieldName> af) {
- editableAccountFields = af;
- }
-
- public List<RegexFindReplace> getCommentLinks() {
- return commentLinks;
- }
-
- public void setCommentLinks(final List<RegexFindReplace> cl) {
- commentLinks = cl;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/GitwebLink.java b/src/main/java/com/google/gerrit/client/data/GitwebLink.java
deleted file mode 100644
index e293e86758..0000000000
--- a/src/main/java/com/google/gerrit/client/data/GitwebLink.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gwt.http.client.URL;
-
-/** Link to an external gitweb server. */
-public class GitwebLink {
- protected String baseUrl;
-
- protected GitwebLink() {
- }
-
- public GitwebLink(final String base) {
- baseUrl = base + "?";
- }
-
- public String toRevision(final Project.NameKey project, final PatchSet ps) {
- final StringBuilder r = new StringBuilder();
- p(r, project);
- a(r, "commit");
- h(r, ps);
- return baseUrl + r;
- }
-
- public String toProject(final Project.NameKey project) {
- final StringBuilder r = new StringBuilder();
- p(r, project);
- a(r, "summary");
- return baseUrl + r;
- }
-
- public String toBranch(final Branch.NameKey branch) {
- final StringBuilder r = new StringBuilder();
- p(r, branch.getParentKey());
- h(r, branch);
- a(r, "shortlog");
- return baseUrl + r;
- }
-
- private static void p(final StringBuilder r, final Project.NameKey project) {
- String n = project.get();
- if (!n.endsWith(".git")) {
- n += ".git";
- }
- var(r, "p", n);
- }
-
- private static void h(final StringBuilder r, final PatchSet ps) {
- var(r, "h", ps.getRevision().get());
- }
-
- private static void h(final StringBuilder r, final Branch.NameKey branch) {
- var(r, "h", branch.get());
- }
-
- private static void a(final StringBuilder r, final String where) {
- var(r, "a", where);
- }
-
- private static void var(final StringBuilder r, final String n, final String v) {
- if (r.length() > 0) {
- r.append(";");
- }
- r.append(n);
- r.append("=");
- r.append(URL.encodeComponent(v));
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/PatchScript.java b/src/main/java/com/google/gerrit/client/data/PatchScript.java
deleted file mode 100644
index 446d9666f4..0000000000
--- a/src/main/java/com/google/gerrit/client/data/PatchScript.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-
-
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.reviewdb.Change;
-
-import org.eclipse.jgit.diff.Edit;
-
-import java.util.List;
-
-public class PatchScript {
- public static enum DisplayMethod {
- NONE, DIFF, IMG
- }
-
- protected Change.Key changeId;
- protected List<String> header;
- protected PatchScriptSettings settings;
- protected SparseFileContent a;
- protected SparseFileContent b;
- protected List<Edit> edits;
- protected DisplayMethod displayMethodA;
- protected DisplayMethod displayMethodB;
-
- public PatchScript(final Change.Key ck, final List<String> h,
- final PatchScriptSettings s, final SparseFileContent ca,
- final SparseFileContent cb, final List<Edit> e, final DisplayMethod ma,
- final DisplayMethod mb) {
- changeId = ck;
- header = h;
- settings = s;
- a = ca;
- b = cb;
- edits = e;
- displayMethodA = ma;
- displayMethodB = mb;
- }
-
- protected PatchScript() {
- }
-
- public Change.Key getChangeId() {
- return changeId;
- }
-
- public DisplayMethod getDisplayMethodA() {
- return displayMethodA;
- }
-
- public DisplayMethod getDisplayMethodB() {
- return displayMethodB;
- }
-
- public List<String> getPatchHeader() {
- return header;
- }
-
- public int getContext() {
- return settings.getContext();
- }
-
- public boolean isIgnoreWhitespace() {
- return settings.getWhitespace() != Whitespace.IGNORE_NONE;
- }
-
- public SparseFileContent getA() {
- return a;
- }
-
- public SparseFileContent getB() {
- return b;
- }
-
- public List<Edit> getEdits() {
- return edits;
- }
-
- public Iterable<EditList.Hunk> getHunks() {
- return new EditList(edits, getContext(), a.size(), b.size()).getHunks();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/PatchScriptSettings.java b/src/main/java/com/google/gerrit/client/data/PatchScriptSettings.java
deleted file mode 100644
index 37862d5018..0000000000
--- a/src/main/java/com/google/gerrit/client/data/PatchScriptSettings.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.rpc.CodedEnum;
-
-public class PatchScriptSettings {
- public static enum Whitespace implements CodedEnum {
- IGNORE_NONE('N'), //
- IGNORE_SPACE_AT_EOL('E'), //
- IGNORE_SPACE_CHANGE('S'), //
- IGNORE_ALL_SPACE('A');
-
- private final char code;
-
- private Whitespace(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
- }
-
- protected int context;
- protected Whitespace whitespace;
-
- public PatchScriptSettings() {
- context = AccountGeneralPreferences.DEFAULT_CONTEXT;
- whitespace = Whitespace.IGNORE_NONE;
- }
-
- public PatchScriptSettings(final PatchScriptSettings s) {
- context = s.context;
- whitespace = s.whitespace;
- }
-
- public int getContext() {
- return context;
- }
-
- public void setContext(final int ctx) {
- assert 0 <= ctx || ctx == AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
- context = ctx;
- }
-
- public Whitespace getWhitespace() {
- return whitespace;
- }
-
- public void setWhitespace(final Whitespace ws) {
- assert ws != null;
- whitespace = ws;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/PatchSetDetail.java b/src/main/java/com/google/gerrit/client/data/PatchSetDetail.java
deleted file mode 100644
index 69a617ac1a..0000000000
--- a/src/main/java/com/google/gerrit/client/data/PatchSetDetail.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-
-import java.util.List;
-
-public class PatchSetDetail {
- protected PatchSet patchSet;
- protected PatchSetInfo info;
- protected List<Patch> patches;
-
- public PatchSetDetail() {
- }
-
- public PatchSet getPatchSet() {
- return patchSet;
- }
-
- public void setPatchSet(final PatchSet ps) {
- patchSet = ps;
- }
-
- public PatchSetInfo getInfo() {
- return info;
- }
-
- public void setInfo(final PatchSetInfo i) {
- info = i;
- }
-
- public List<Patch> getPatches() {
- return patches;
- }
-
- public void setPatches(final List<Patch> p) {
- patches = p;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/ProjectInfo.java b/src/main/java/com/google/gerrit/client/data/ProjectInfo.java
deleted file mode 100644
index e56efd37ac..0000000000
--- a/src/main/java/com/google/gerrit/client/data/ProjectInfo.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.Project;
-
-public class ProjectInfo {
- protected Project.NameKey key;
-
- protected ProjectInfo() {
- }
-
- public ProjectInfo(final Project.NameKey key) {
- this.key = key;
- }
-
- public Project.NameKey getKey() {
- return key;
- }
-
- public String getName() {
- return key.get();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/SingleListChangeInfo.java b/src/main/java/com/google/gerrit/client/data/SingleListChangeInfo.java
deleted file mode 100644
index 26543e362c..0000000000
--- a/src/main/java/com/google/gerrit/client/data/SingleListChangeInfo.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import java.util.List;
-
-/** Summary information needed for screens showing a single list of changes}. */
-public class SingleListChangeInfo {
- protected AccountInfoCache accounts;
- protected List<ChangeInfo> changes;
- protected boolean atEnd;
-
- public SingleListChangeInfo() {
- }
-
- public AccountInfoCache getAccounts() {
- return accounts;
- }
-
- public void setAccounts(final AccountInfoCache ac) {
- accounts = ac;
- }
-
- public List<ChangeInfo> getChanges() {
- return changes;
- }
-
- public boolean isAtEnd() {
- return atEnd;
- }
-
- public void setChanges(List<ChangeInfo> c) {
- setChanges(c, true);
- }
-
- public void setChanges(List<ChangeInfo> c, boolean end) {
- changes = c;
- atEnd = end;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/SparseFileContent.java b/src/main/java/com/google/gerrit/client/data/SparseFileContent.java
deleted file mode 100644
index 358692dc2c..0000000000
--- a/src/main/java/com/google/gerrit/client/data/SparseFileContent.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class SparseFileContent {
- protected List<Range> ranges;
- protected int size;
- protected boolean missingNewlineAtEnd;
-
- private transient int currentRangeIdx;
-
- public SparseFileContent() {
- ranges = new ArrayList<Range>();
- }
-
- public int size() {
- return size;
- }
-
- public void setSize(final int s) {
- size = s;
- }
-
- public boolean isMissingNewlineAtEnd() {
- return missingNewlineAtEnd;
- }
-
- public void setMissingNewlineAtEnd(final boolean missing) {
- missingNewlineAtEnd = missing;
- }
-
- public String get(final int idx) {
- final String line = getLine(idx);
- if (line == null) {
- throw new ArrayIndexOutOfBoundsException(idx);
- }
- return line;
- }
-
- public boolean contains(final int idx) {
- return getLine(idx) != null;
- }
-
- private String getLine(final int idx) {
- // Most requests are sequential in nature, fetching the next
- // line from the current range, or the next range.
- //
- int high = ranges.size();
- if (currentRangeIdx < high) {
- Range cur = ranges.get(currentRangeIdx);
- if (cur.contains(idx)) {
- return cur.get(idx);
- }
-
- if (++currentRangeIdx < high) {
- final Range next = ranges.get(currentRangeIdx);
- if (next.contains(idx)) {
- return next.get(idx);
- }
- }
- }
-
- // Binary search for the range, since we know its a sorted list.
- //
- int low = 0;
- do {
- final int mid = (low + high) / 2;
- final Range cur = ranges.get(mid);
- if (cur.contains(idx)) {
- currentRangeIdx = mid;
- return cur.get(idx);
- }
- if (idx < cur.base)
- high = mid;
- else
- low = mid + 1;
- } while (low < high);
- return null;
- }
-
- public void addLine(final int i, final String content) {
- final Range r;
- if (!ranges.isEmpty() && i == last().end()) {
- r = last();
- } else {
- r = new Range(i);
- ranges.add(r);
- }
- r.lines.add(content);
- }
-
- private Range last() {
- return ranges.get(ranges.size() - 1);
- }
-
- @Override
- public String toString() {
- final StringBuilder b = new StringBuilder();
- b.append("SparseFileContent[\n");
- for (Range r : ranges) {
- b.append(" ");
- b.append(r.toString());
- b.append('\n');
- }
- b.append("]");
- return b.toString();
- }
-
- static class Range {
- protected int base;
- protected List<String> lines;
-
- private Range(final int b) {
- base = b;
- lines = new ArrayList<String>();
- }
-
- protected Range() {
- }
-
- private String get(final int i) {
- return lines.get(i - base);
- }
-
- private int end() {
- return base + lines.size();
- }
-
- private boolean contains(final int i) {
- return base <= i && i < end();
- }
-
- @Override
- public String toString() {
- return "Range[" + base + "," + end() + ")";
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/SshHostKey.java b/src/main/java/com/google/gerrit/client/data/SshHostKey.java
deleted file mode 100644
index 5ea37c9e30..0000000000
--- a/src/main/java/com/google/gerrit/client/data/SshHostKey.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-/** Description of the SSH daemon host key used by Gerrit. */
-public class SshHostKey {
- protected String hostIdent;
- protected String hostKey;
- protected String fingerprint;
-
- protected SshHostKey() {
- }
-
- public SshHostKey(final String hi, final String hk, final String fp) {
- hostIdent = hi;
- hostKey = hk;
- fingerprint = fp;
- }
-
- /** @return host name string, to appear in a known_hosts file. */
- public String getHostIdent() {
- return hostIdent;
- }
-
- /** @return base 64 encoded host key string, starting with key type. */
- public String getHostKey() {
- return hostKey;
- }
-
- /** @return the key fingerprint, as displayed by a connecting client. */
- public String getFingerprint() {
- return fingerprint;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/data/SystemInfoService.java b/src/main/java/com/google/gerrit/client/data/SystemInfoService.java
deleted file mode 100644
index d05dab13ef..0000000000
--- a/src/main/java/com/google/gerrit/client/data/SystemInfoService.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.data;
-
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.AllowCrossSiteRequest;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-import java.util.List;
-
-public interface SystemInfoService extends RemoteJsonService {
- @AllowCrossSiteRequest
- void daemonHostKeys(AsyncCallback<List<SshHostKey>> callback);
-
- @SignInRequired
- void contributorAgreements(AsyncCallback<List<ContributorAgreement>> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java b/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
deleted file mode 100644
index 0776b5e0aa..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/AbstractPatchContentTable.java
+++ /dev/null
@@ -1,609 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.PatchTable;
-import com.google.gerrit.client.changes.PublishCommentScreen;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.SparseFileContent;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.ui.CommentPanel;
-import com.google.gerrit.client.ui.NavigationTable;
-import com.google.gerrit.client.ui.NeedsSignInKeyCommand;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.UIObject;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class AbstractPatchContentTable extends NavigationTable<Object> {
- protected PatchTable fileList;
- protected AccountInfoCache accountCache = AccountInfoCache.empty();
- protected Patch.Key patchKey;
- protected PatchSet.Id idSideA;
- protected PatchSet.Id idSideB;
- protected boolean onlyOneHunk;
- protected String formatLanguage;
-
- private final KeyCommandSet keysComment;
- private HandlerRegistration regComment;
-
- protected AbstractPatchContentTable() {
- keysNavigation.add(new PrevKeyCommand(0, 'k', PatchUtil.C.linePrev()));
- keysNavigation.add(new NextKeyCommand(0, 'j', PatchUtil.C.lineNext()));
- keysNavigation.add(new PrevChunkKeyCmd(0, 'p', PatchUtil.C.chunkPrev()));
- keysNavigation.add(new NextChunkKeyCmd(0, 'n', PatchUtil.C.chunkNext()));
-
- keysAction.add(new OpenKeyCommand(0, 'o', PatchUtil.C.expandComment()));
- keysAction.add(new OpenKeyCommand(0, KeyCodes.KEY_ENTER, PatchUtil.C
- .expandComment()));
-
- if (Gerrit.isSignedIn()) {
- keysAction.add(new InsertCommentCommand(0, 'c', PatchUtil.C
- .commentInsert()));
- keysAction.add(new PublishCommentsKeyCommand(0, 'r', Util.C
- .keyPublishComments()));
-
- // See CommentEditorPanel
- //
- keysComment = new KeyCommandSet(PatchUtil.C.commentEditorSet());
- keysComment.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 's', PatchUtil.C
- .commentSaveDraft()));
- keysComment.add(new NoOpKeyCommand(KeyCommand.M_CTRL, 'd', PatchUtil.C
- .commentDiscard()));
- keysComment.add(new NoOpKeyCommand(0, KeyCodes.KEY_ESCAPE, PatchUtil.C
- .commentCancelEdit()));
- } else {
- keysComment = null;
- }
-
- table.setStyleName("gerrit-PatchContentTable");
- }
-
- void notifyDraftDelta(final int delta) {
- if (fileList != null) {
- fileList.notifyDraftDelta(patchKey, delta);
- }
- }
-
- @Override
- public void setRegisterKeys(final boolean on) {
- super.setRegisterKeys(on);
- if (on && keysComment != null && regComment == null) {
- regComment = GlobalKey.add(this, keysComment);
- } else if (!on && regComment != null) {
- regComment.removeHandler();
- regComment = null;
- }
- }
-
- public void display(final Patch.Key k, final PatchSet.Id a,
- final PatchSet.Id b, final PatchScript s) {
- patchKey = k;
- idSideA = a;
- idSideB = b;
-
- final String pathName = patchKey.get();
- int ext = pathName.lastIndexOf('.');
- formatLanguage = ext > 0 ? pathName.substring(ext + 1).toLowerCase() : null;
-
- render(s);
- }
-
- protected abstract void render(PatchScript script);
-
- protected abstract void onInsertComment(PatchLine pl);
-
- public abstract void display(CommentDetail comments);
-
- @Override
- protected MyFlexTable createFlexTable() {
- return new DoubleClickFlexTable();
- }
-
- @Override
- protected Object getRowItemKey(final Object item) {
- return null;
- }
-
- protected void initScript(final PatchScript script) {
- if (script.getEdits().size() == 1) {
- final SparseFileContent a = script.getA();
- final SparseFileContent b = script.getB();
- onlyOneHunk = a.size() == 0 || b.size() == 0;
- } else {
- onlyOneHunk = false;
- }
- }
-
- private boolean isChunk(final int row) {
- final Object o = getRowItem(row);
- if (!onlyOneHunk && o instanceof PatchLine) {
- final PatchLine pl = (PatchLine) o;
- switch (pl.getType()) {
- case DELETE:
- case INSERT:
- case REPLACE:
- return true;
- }
- } else if (o instanceof CommentList) {
- return true;
- }
- return false;
- }
-
- private int findChunkStart(int row) {
- while (0 <= row && isChunk(row)) {
- row--;
- }
- return row + 1;
- }
-
- private int findChunkEnd(int row) {
- final int max = table.getRowCount();
- while (row < max && isChunk(row)) {
- row++;
- }
- return row - 1;
- }
-
- private static int oneBefore(final int begin) {
- return 1 <= begin ? begin - 1 : begin;
- }
-
- private int oneAfter(final int end) {
- return end + 1 < table.getRowCount() ? end + 1 : end;
- }
-
- private void moveToPrevChunk(int row) {
- while (0 <= row && isChunk(row)) {
- row--;
- }
- for (; 0 <= row; row--) {
- if (isChunk(row)) {
- final int start = findChunkStart(row);
- movePointerTo(start, false);
- scrollIntoView(oneBefore(start), oneAfter(row));
- return;
- }
- }
-
- // No prior hunk found? Try to hit the first line in the file.
- //
- for (row = 0; row < table.getRowCount(); row++) {
- if (getRowItem(row) != null) {
- movePointerTo(row);
- break;
- }
- }
- }
-
- private void moveToNextChunk(int row) {
- final int max = table.getRowCount();
- while (row < max && isChunk(row)) {
- row++;
- }
- for (; row < max; row++) {
- if (isChunk(row)) {
- movePointerTo(row, false);
- scrollIntoView(oneBefore(row), oneAfter(findChunkEnd(row)));
- return;
- }
- }
-
- // No next hunk found? Try to hit the last line in the file.
- //
- for (row = max - 1; row >= 0; row--) {
- if (getRowItem(row) != null) {
- movePointerTo(row);
- break;
- }
- }
- }
-
- /** Invoked when the user clicks on a table cell. */
- protected abstract void onCellDoubleClick(int row, int column);
-
- /**
- * Invokes createCommentEditor() with an empty string as value for the comment
- * parent UUID. This method is invoked by callers that want to create an
- * editor for a comment that is not a reply.
- */
- protected void createCommentEditor(final int suggestRow, final int column,
- final int line, final short file) {
- createCommentEditor(suggestRow, column, line, file, null /* no parent */);
- }
-
- protected void createReplyEditor(final PublishedCommentPanel currentPanel) {
- final int row = rowOf(currentPanel.getElement());
- if (row >= 0) {
- final int column = columnOf(currentPanel.getElement());
- final PatchLineComment c = currentPanel.comment;
- final String uuid = c.getKey().get();
- final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
- final short file;
- if (idSideA == null) {
- file = c.getSide();
- } else if (idSideB.equals(psId)) {
- file = 1;
- } else {
- file = 0;
- }
- createCommentEditor(row, column, c.getLine(), file, uuid);
- }
- }
-
- private void createCommentEditor(final int suggestRow, final int column,
- final int line, final short file, final String parentUuid) {
- if (line < 1) {
- // Refuse to create an editor before the start of the file.
- //
- return;
- }
-
- int row = suggestRow;
- if (parentUuid != null) {
- row++;
- } else {
- int spans[] = new int[column + 1];
- OUTER: while (row < table.getRowCount()) {
- int col = 0;
- for (int cell = 0; row < table.getRowCount()
- && cell < table.getCellCount(row); cell++) {
- while (col < column && 0 < spans[col]) {
- spans[col++]--;
- }
- spans[col] = table.getFlexCellFormatter().getRowSpan(row, cell);
- if (col == column) {
- final Widget w = table.getWidget(row, cell);
- if (w instanceof CommentEditorPanel) {
- break OUTER;
- } else if (w instanceof CommentPanel) {
- row++;
- } else {
- break OUTER;
- }
- }
- }
- }
- }
- if (row < table.getRowCount() && column < table.getCellCount(row)
- && table.getWidget(row, column) instanceof CommentEditorPanel) {
- // Don't insert two editors on the same position, it doesn't make
- // any sense to the user.
- //
- ((CommentEditorPanel) table.getWidget(row, column)).setFocus(true);
- return;
- }
-
- if (!Gerrit.isSignedIn()) {
- Gerrit.doSignIn();
- return;
- }
-
- final Patch.Key parentKey;
- final short side;
- switch (file) {
- case 0:
- if (idSideA == null) {
- parentKey = new Patch.Key(idSideB, patchKey.get());
- side = (short) 0;
- } else {
- parentKey = new Patch.Key(idSideA, patchKey.get());
- side = (short) 1;
- }
- break;
- case 1:
- parentKey = new Patch.Key(idSideB, patchKey.get());
- side = (short) 1;
- break;
- default:
- throw new RuntimeException("unexpected file id " + file);
- }
-
- final PatchLineComment newComment =
- new PatchLineComment(new PatchLineComment.Key(parentKey, null), line,
- Gerrit.getUserAccount().getId(), parentUuid);
- newComment.setSide(side);
- newComment.setMessage("");
-
- final CommentEditorPanel ed = new CommentEditorPanel(newComment);
- boolean isCommentRow = false;
- boolean needInsert = false;
- if (row < table.getRowCount()) {
- for (int cell = 0; cell < table.getCellCount(row); cell++) {
- final Widget w = table.getWidget(row, cell);
- if (w instanceof CommentEditorPanel || w instanceof CommentPanel) {
- if (column == cell) {
- needInsert = true;
- }
- isCommentRow = true;
- }
- }
- }
- if (needInsert || !isCommentRow) {
- insertRow(row);
- styleCommentRow(row);
- }
- table.setWidget(row, column, ed);
-
- int span = 1;
- for (int r = row + 1; r < table.getRowCount(); r++) {
- boolean hasComment = false;
- for (int c = 0; c < table.getCellCount(r); c++) {
- final Widget w = table.getWidget(r, c);
- if (w instanceof CommentPanel || w instanceof CommentEditorPanel) {
- if (c != column) {
- hasComment = true;
- break;
- }
- }
- }
- if (hasComment) {
- table.removeCell(r, column);
- span++;
- } else {
- break;
- }
- }
- if (span > 1) {
- table.getFlexCellFormatter().setRowSpan(row, column, span);
- }
-
- for (int r = row - 1; r > 0; r--) {
- if (getRowItem(r) instanceof CommentList) {
- continue;
- } else if (getRowItem(r) != null) {
- movePointerTo(r);
- break;
- }
- }
-
- ed.setFocus(true);
- }
-
- protected void insertRow(final int row) {
- table.insertRow(row);
- table.getCellFormatter().setStyleName(row, 0, S_ICON_CELL);
- }
-
- @Override
- protected void onOpenRow(final int row) {
- final Object item = getRowItem(row);
- if (item instanceof CommentList) {
- for (final CommentPanel p : ((CommentList) item).panels) {
- p.setOpen(!p.isOpen());
- }
- }
- }
-
- public void setAccountInfoCache(final AccountInfoCache aic) {
- assert aic != null;
- accountCache = aic;
- }
-
- static void destroyEditor(final FlexTable table, final int row, final int col) {
- table.clearCell(row, col);
- final int span = table.getFlexCellFormatter().getRowSpan(row, col);
- boolean removeRow = true;
- final int nCells = table.getCellCount(row);
- for (int cell = 0; cell < nCells; cell++) {
- if (table.getWidget(row, cell) != null) {
- removeRow = false;
- break;
- }
- }
- if (removeRow) {
- for (int r = row - 1; 0 <= r; r--) {
- boolean data = false;
- for (int c = 0; c < table.getCellCount(r); c++) {
- data |= table.getWidget(r, c) != null;
- final int s = table.getFlexCellFormatter().getRowSpan(r, c) - 1;
- if (r + s == row) {
- table.getFlexCellFormatter().setRowSpan(r, c, s);
- }
- }
- if (!data) {
- break;
- }
- }
- table.removeRow(row);
- } else if (span != 1) {
- table.getFlexCellFormatter().setRowSpan(row, col, 1);
- for (int r = row + 1; r < row + span; r++) {
- table.insertCell(r, col + 1);
- }
- }
- }
-
- protected void bindComment(final int row, final int col,
- final PatchLineComment line, final boolean isLast) {
- if (line.getStatus() == PatchLineComment.Status.DRAFT) {
- final CommentEditorPanel plc = new CommentEditorPanel(line);
- table.setWidget(row, col, plc);
-
- } else {
- final AccountInfo author = accountCache.get(line.getAuthor());
- final PublishedCommentPanel panel =
- new PublishedCommentPanel(author, line);
- table.setWidget(row, col, panel);
-
- CommentList l = (CommentList) getRowItem(row);
- if (l == null) {
- l = new CommentList();
- setRowItem(row, l);
- }
- l.comments.add(line);
- l.panels.add(panel);
- }
-
- styleCommentRow(row);
- }
-
- private void styleCommentRow(final int row) {
- final CellFormatter fmt = table.getCellFormatter();
- final Element iconCell = fmt.getElement(row, 0);
- UIObject.setStyleName(DOM.getParent(iconCell), "CommentHolder", true);
- }
-
- protected static class CommentList {
- final List<PatchLineComment> comments = new ArrayList<PatchLineComment>();
- final List<PublishedCommentPanel> panels =
- new ArrayList<PublishedCommentPanel>();
- }
-
- protected class DoubleClickFlexTable extends MyFlexTable {
- public DoubleClickFlexTable() {
- sinkEvents(Event.ONDBLCLICK | Event.ONCLICK);
- }
-
- @Override
- public void onBrowserEvent(final Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONCLICK: {
- // Find out which cell was actually clicked.
- final Element td = getEventTargetCell(event);
- if (td == null) {
- break;
- }
- final int row = rowOf(td);
- if (getRowItem(row) != null) {
- movePointerTo(row);
- return;
- }
- break;
- }
- case Event.ONDBLCLICK: {
- // Find out which cell was actually clicked.
- Element td = getEventTargetCell(event);
- if (td == null) {
- return;
- }
- onCellDoubleClick(rowOf(td), columnOf(td));
- return;
- }
- }
- super.onBrowserEvent(event);
- }
- }
-
- public static class NoOpKeyCommand extends NeedsSignInKeyCommand {
- public NoOpKeyCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- }
- }
-
- public class InsertCommentCommand extends NeedsSignInKeyCommand {
- public InsertCommentCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- ensurePointerVisible();
- for (int row = getCurrentRow(); 0 <= row; row--) {
- final Object item = getRowItem(row);
- if (item instanceof PatchLine) {
- onInsertComment((PatchLine) item);
- return;
- } else if (item instanceof CommentList) {
- continue;
- } else {
- return;
- }
- }
- }
- }
-
- public class PublishCommentsKeyCommand extends NeedsSignInKeyCommand {
- public PublishCommentsKeyCommand(int mask, char key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- final PatchSet.Id id = patchKey.getParentKey();
- Gerrit.display("change,publish," + id.toString(),
- new PublishCommentScreen(id));
- }
- }
-
- public class PrevChunkKeyCmd extends KeyCommand {
- public PrevChunkKeyCmd(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- ensurePointerVisible();
- moveToPrevChunk(getCurrentRow());
- }
- }
-
- public class NextChunkKeyCmd extends KeyCommand {
- public NextChunkKeyCmd(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- ensurePointerVisible();
- moveToNextChunk(getCurrentRow());
- }
- }
-
- private class PublishedCommentPanel extends CommentPanel implements
- ClickHandler {
- final PatchLineComment comment;
-
- PublishedCommentPanel(final AccountInfo author, final PatchLineComment c) {
- super(author, c.getWrittenOn(), c.getMessage());
- this.comment = c;
-
- final Button reply = new Button(PatchUtil.C.buttonReply());
- reply.addClickHandler(this);
- getButtonPanel().add(reply);
- }
-
- @Override
- public void onClick(final ClickEvent event) {
- createReplyEditor(this);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/AddReviewerResult.java b/src/main/java/com/google/gerrit/client/patches/AddReviewerResult.java
deleted file mode 100644
index d6f0dfe796..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/AddReviewerResult.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.data.ChangeDetail;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Result from adding a reviewer to a change. */
-public class AddReviewerResult {
- protected List<Error> errors;
- protected ChangeDetail change;
-
- public AddReviewerResult() {
- errors = new ArrayList<Error>();
- }
-
- public void addError(final Error e) {
- errors.add(e);
- }
-
- public List<Error> getErrors() {
- return errors;
- }
-
- public ChangeDetail getChange() {
- return change;
- }
-
- public void setChange(final ChangeDetail d) {
- change = d;
- }
-
- public static class Error {
- public static enum Type {
- /** Name supplied does not match to a registered account. */
- ACCOUNT_NOT_FOUND,
-
- /** The account is not permitted to see the change. */
- CHANGE_NOT_VISIBLE
- }
-
- protected Type type;
- protected String name;
-
- protected Error() {
- }
-
- public Error(final Type type, final String who) {
- this.type = type;
- this.name = who;
- }
-
- public Type getType() {
- return type;
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return type + " " + name;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/CommentDetail.java b/src/main/java/com/google/gerrit/client/patches/CommentDetail.java
deleted file mode 100644
index 7e417609cc..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/CommentDetail.java
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class CommentDetail {
- protected List<PatchLineComment> commentsA;
- protected List<PatchLineComment> commentsB;
- protected List<Patch> history;
- protected AccountInfoCache accounts;
-
- private transient PatchSet.Id idA;
- private transient PatchSet.Id idB;
- private transient Map<Integer, List<PatchLineComment>> forA;
- private transient Map<Integer, List<PatchLineComment>> forB;
-
- public CommentDetail(final PatchSet.Id a, final PatchSet.Id b) {
- commentsA = new ArrayList<PatchLineComment>();
- commentsB = new ArrayList<PatchLineComment>();
-
- idA = a;
- idB = b;
- }
-
- protected CommentDetail() {
- }
-
- public boolean include(final PatchLineComment p) {
- final PatchSet.Id psId = p.getKey().getParentKey().getParentKey();
- switch (p.getSide()) {
- case 0:
- if (idA == null && idB.equals(psId)) {
- commentsA.add(p);
- return true;
- }
- break;
-
- case 1:
- if (idA != null && idA.equals(psId)) {
- commentsA.add(p);
- return true;
- }
-
- if (idB.equals(psId)) {
- commentsB.add(p);
- return true;
- }
- break;
- }
- return false;
- }
-
- public void setAccountInfoCache(final AccountInfoCache a) {
- accounts = a;
- }
-
- public void setHistory(final List<Patch> h) {
- history = h;
- }
-
- public AccountInfoCache getAccounts() {
- return accounts;
- }
-
- public List<Patch> getHistory() {
- return history;
- }
-
- public List<PatchLineComment> getCommentsA() {
- return commentsA;
- }
-
- public List<PatchLineComment> getCommentsB() {
- return commentsB;
- }
-
- public boolean isEmpty() {
- return commentsA.isEmpty() && commentsB.isEmpty();
- }
-
- public List<PatchLineComment> getForA(final int lineNbr) {
- if (lineNbr == 0) {
- return Collections.emptyList();
- }
- if (forA == null) {
- forA = index(commentsA);
- }
- return get(forA, lineNbr);
- }
-
- public List<PatchLineComment> getForB(final int lineNbr) {
- if (lineNbr == 0) {
- return Collections.emptyList();
- }
- if (forB == null) {
- forB = index(commentsB);
- }
- return get(forB, lineNbr);
- }
-
- private static List<PatchLineComment> get(
- final Map<Integer, List<PatchLineComment>> m, final int i) {
- final List<PatchLineComment> r = m.get(i);
- return r != null ? orderComments(r) : Collections.<PatchLineComment> emptyList();
- }
-
- /**
- * Order the comments based on their parent_uuid parent. It is possible to do this by
- * iterating over the list only once but it's probably overkill since the number of comments
- * on a given line will be small most of the time.
- *
- * @param comments The list of comments for a given line.
- * @return The comments sorted as they should appear in the UI
- */
- private static List<PatchLineComment> orderComments(List<PatchLineComment> comments) {
- // Map of comments keyed by their parent. The values are lists of comments since it is
- // possible for several comments to have the same parent (this can happen if two reviewers
- // click Reply on the same comment at the same time). Such comments will be displayed under
- // their correct parent in chronological order.
- Map<String, List<PatchLineComment>> parentMap = new HashMap<String, List<PatchLineComment>>();
-
- // It's possible to have more than one root comment if two reviewers create a comment on the
- // same line at the same time
- List<PatchLineComment> rootComments = new ArrayList<PatchLineComment>();
-
- // Store all the comments in parentMap, keyed by their parent
- for (PatchLineComment c : comments) {
- String parentUuid = c.getParentUuid();
- List<PatchLineComment> l = parentMap.get(parentUuid);
- if (l == null) {
- l = new ArrayList<PatchLineComment>();
- parentMap.put(parentUuid, l);
- }
- l.add(c);
- if (parentUuid == null) rootComments.add(c);
- }
-
- // Add the comments in the list, starting with the head and then going through all the
- // comments that have it as a parent, and so on
- List<PatchLineComment> result = new ArrayList<PatchLineComment>();
- addChildren(parentMap, rootComments, result);
-
- return result;
- }
-
- /**
- * Add the comments to <code>outResult</code>, depth first
- */
- private static void addChildren(Map<String, List<PatchLineComment>> parentMap,
- List<PatchLineComment> children, List<PatchLineComment> outResult) {
- if (children != null) {
- for (PatchLineComment c : children) {
- outResult.add(c);
- addChildren(parentMap, parentMap.get(c.getKey().get()), outResult);
- }
- }
- }
-
- private Map<Integer, List<PatchLineComment>> index(
- final List<PatchLineComment> in) {
- final HashMap<Integer, List<PatchLineComment>> r;
-
- r = new HashMap<Integer, List<PatchLineComment>>();
- for (final PatchLineComment p : in) {
- List<PatchLineComment> l = r.get(p.getLine());
- if (l == null) {
- l = new ArrayList<PatchLineComment>();
- r.put(p.getLine(), l);
- }
- l.add(p);
- }
- return r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java b/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
deleted file mode 100644
index 8353c0e235..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/CommentEditorPanel.java
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.CommentPanel;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.event.dom.client.KeyCodes;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.dom.client.KeyPressHandler;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Timer;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.Button;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.Focusable;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.globalkey.client.NpTextArea;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.sql.Timestamp;
-
-public class CommentEditorPanel extends CommentPanel implements ClickHandler,
- DoubleClickHandler {
- private static final int INITIAL_COLS = 60;
- private static final int INITIAL_LINES = 5;
- private static final int MAX_LINES = 30;
- private static final AsyncCallback<VoidResult> NULL_CALLBACK =
- new AsyncCallback<VoidResult>() {
- @Override
- public void onFailure(Throwable caught) {
- }
-
- @Override
- public void onSuccess(VoidResult result) {
- }
- };
-
- private PatchLineComment comment;
-
- private final NpTextArea text;
- private final Button edit;
- private final Button save;
- private final Button cancel;
- private final Button discard;
- private final Timer expandTimer;
-
- public CommentEditorPanel(final PatchLineComment plc) {
- comment = plc;
-
- addStyleName("gerrit-CommentEditorPanel");
- setAuthorNameText(PatchUtil.C.draft());
- setMessageText(plc.getMessage());
- addDoubleClickHandler(this);
-
- expandTimer = new Timer() {
- @Override
- public void run() {
- expandText();
- }
- };
- text = new NpTextArea();
- text.setText(comment.getMessage());
- text.setCharacterWidth(INITIAL_COLS);
- text.setVisibleLines(INITIAL_LINES);
- DOM.setElementPropertyBoolean(text.getElement(), "spellcheck", true);
- text.addKeyPressHandler(new KeyPressHandler() {
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (event.getCharCode() == KeyCodes.KEY_ESCAPE
- && !event.isAnyModifierKeyDown()) {
- event.preventDefault();
- if (isNew()) {
- onDiscard();
- } else {
- render();
- }
- return;
- }
-
- if ((event.isControlKeyDown() || event.isMetaKeyDown())
- && !event.isAltKeyDown() && !event.isShiftKeyDown()) {
- switch (event.getCharCode()) {
- case 's':
- event.preventDefault();
- onSave(NULL_CALLBACK);
- return;
-
- case 'd':
- event.preventDefault();
- if (isNew()) {
- onDiscard();
- } else if (Window.confirm(PatchUtil.C.confirmDiscard())) {
- onDiscard();
- } else {
- text.setFocus(true);
- }
- return;
- }
- }
-
- expandTimer.schedule(250);
- }
- });
- addContent(text);
-
- edit = new Button();
- edit.setText(PatchUtil.C.buttonEdit());
- edit.addClickHandler(this);
- getButtonPanel().add(edit);
-
- save = new Button();
- save.setText(PatchUtil.C.buttonSave());
- save.addClickHandler(this);
- getButtonPanel().add(save);
-
- cancel = new Button();
- cancel.setText(PatchUtil.C.buttonCancel());
- cancel.addClickHandler(this);
- getButtonPanel().add(cancel);
-
- discard = new Button();
- discard.setText(PatchUtil.C.buttonDiscard());
- discard.addClickHandler(this);
- getButtonPanel().add(discard);
-
- setOpen(true);
- if (isNew()) {
- edit();
- } else {
- render();
- }
- }
-
- private void expandText() {
- final double cols = text.getCharacterWidth();
- int rows = 2;
- for (final String line : text.getText().split("\n")) {
- rows += Math.ceil((1.0 + line.length()) / cols);
- }
- rows = Math.max(INITIAL_LINES, Math.min(rows, MAX_LINES));
- if (text.getVisibleLines() != rows) {
- text.setVisibleLines(rows);
- }
- }
-
- private void edit() {
- if (!isOpen()) {
- setOpen(true);
- }
- text.setText(comment.getMessage());
- expandText();
- stateEdit(true);
- text.setFocus(true);
- }
-
- private void render() {
- final Timestamp on = comment.getWrittenOn();
- setDateText(PatchUtil.M.draftSaved(new java.util.Date(on.getTime())));
- setMessageText(comment.getMessage());
- stateEdit(false);
- }
-
- private void stateEdit(final boolean inEdit) {
- expandTimer.cancel();
- setMessageTextVisible(!inEdit);
- edit.setVisible(!inEdit);
-
- text.setVisible(inEdit);
- save.setVisible(inEdit);
- cancel.setVisible(inEdit && !isNew());
- discard.setVisible(inEdit);
- }
-
- void setFocus(final boolean take) {
- if (take && !isOpen()) {
- setOpen(true);
- }
- if (text.isVisible()) {
- text.setFocus(take);
- } else if (take) {
- edit();
- }
- }
-
- boolean isNew() {
- return comment.getKey().get() == null;
- }
-
- @Override
- public void onDoubleClick(final DoubleClickEvent event) {
- edit();
- }
-
- @Override
- public void onClick(final ClickEvent event) {
- final Widget sender = (Widget) event.getSource();
- if (sender == edit) {
- edit();
-
- } else if (sender == save) {
- onSave(NULL_CALLBACK);
-
- } else if (sender == cancel) {
- render();
-
- } else if (sender == discard) {
- onDiscard();
- }
- }
-
- public void saveDraft(AsyncCallback<VoidResult> onSave) {
- if (isOpen() && text.isVisible()) {
- onSave(onSave);
- } else {
- onSave.onSuccess(VoidResult.INSTANCE);
- }
- }
-
- private void onSave(final AsyncCallback<VoidResult> onSave) {
- expandTimer.cancel();
- final String txt = text.getText().trim();
- if ("".equals(txt)) {
- return;
- }
-
- comment.setMessage(txt);
- text.setReadOnly(true);
- save.setEnabled(false);
- cancel.setEnabled(false);
- discard.setEnabled(false);
-
- PatchUtil.DETAIL_SVC.saveDraft(comment,
- new GerritCallback<PatchLineComment>() {
- public void onSuccess(final PatchLineComment result) {
- if (isNew()) {
- notifyDraftDelta(1);
- }
- comment = result;
- text.setReadOnly(false);
- save.setEnabled(true);
- cancel.setEnabled(true);
- discard.setEnabled(true);
- render();
- onSave.onSuccess(VoidResult.INSTANCE);
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- text.setReadOnly(false);
- save.setEnabled(true);
- cancel.setEnabled(true);
- discard.setEnabled(true);
- super.onFailure(caught);
- onSave.onFailure(caught);
- }
- });
- }
-
- private void notifyDraftDelta(final int delta) {
- Widget p = getParent();
- if (p != null) {
- p = p.getParent();
- if (p != null) {
- ((AbstractPatchContentTable) p).notifyDraftDelta(delta);
- }
- }
- }
-
- private void onDiscard() {
- expandTimer.cancel();
- if (isNew()) {
- removeUI();
- return;
- }
-
- text.setReadOnly(true);
- save.setEnabled(false);
- cancel.setEnabled(false);
- discard.setEnabled(false);
-
- PatchUtil.DETAIL_SVC.deleteDraft(comment.getKey(),
- new GerritCallback<VoidResult>() {
- public void onSuccess(final VoidResult result) {
- notifyDraftDelta(-1);
- removeUI();
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- text.setReadOnly(false);
- save.setEnabled(true);
- cancel.setEnabled(true);
- discard.setEnabled(true);
- super.onFailure(caught);
- }
- });
- }
-
- private void removeUI() {
- final FlexTable table = (FlexTable) getParent();
- final int nRows = table.getRowCount();
- for (int row = 0; row < nRows; row++) {
- final int nCells = table.getCellCount(row);
- for (int cell = 0; cell < nCells; cell++) {
- if (table.getWidget(row, cell) == this) {
- AbstractPatchContentTable.destroyEditor(table, row, cell);
- Widget p = table;
- while (p != null) {
- if (p instanceof Focusable) {
- ((Focusable) p).setFocus(true);
- break;
- }
- p = p.getParent();
- }
- return;
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/HistoryTable.java b/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
deleted file mode 100644
index 05059a3cad..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.ui.FancyFlexTable;
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.RadioButton;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A table used to specify which two patch sets should be diff'ed.
- */
-class HistoryTable extends FancyFlexTable<Patch> {
- private final PatchScreen screen;
- final List<HistoryRadio> all = new ArrayList<HistoryRadio>();
-
- HistoryTable(final PatchScreen parent) {
- setStyleName("gerrit-PatchHistoryTable");
- screen = parent;
- table.addStyleName("gerrit-ChangeTable");
- }
-
- void onClick(final HistoryRadio b) {
- switch (b.file) {
- case 0:
- screen.setSideA(b.patchSetId);
- break;
- case 1:
- screen.setSideB(b.patchSetId);
- break;
- default:
- return;
- }
-
- enableAll(false);
- screen.refresh(false);
- }
-
- void enableAll(final boolean on) {
- for (final HistoryRadio a : all) {
- a.setEnabled(on);
- }
- }
-
- void display(final List<Patch> result) {
- all.clear();
-
- final SafeHtmlBuilder nc = new SafeHtmlBuilder();
- appendHeader(nc);
- appendRow(nc, null);
- for (final Patch k : result) {
- appendRow(nc, k);
- }
- resetHtml(nc);
-
- int row = 1;
- {
- final Patch k = new Patch(new Patch.Key(null, ""));
- setRowItem(row, k);
- installRadio(row, k, 0, screen.idSideA);
- row++;
- }
- for (final Patch k : result) {
- setRowItem(row, k);
- installRadio(row, k, 0, screen.idSideA);
- installRadio(row, k, 1, screen.idSideB);
- row++;
- }
- }
-
- private void installRadio(final int row, final Patch k, final int file,
- final PatchSet.Id cur) {
- final PatchSet.Id psid = k.getKey().getParentKey();
- final HistoryRadio b = new HistoryRadio(psid, file);
- b.setValue(eq(cur, psid));
-
- final int cell = radioCell(file);
- table.setWidget(row, cell, b);
- table.getCellFormatter().setHorizontalAlignment(row, cell,
- HasHorizontalAlignment.ALIGN_CENTER);
- all.add(b);
- }
-
- private boolean eq(final PatchSet.Id cur, final PatchSet.Id psid) {
- if (cur == null && psid == null) {
- return true;
- }
- return psid != null && psid.equals(cur);
- }
-
- private int radioCell(final int file) {
- return 2 + file;
- }
-
- private void appendHeader(final SafeHtmlBuilder m) {
- m.openTr();
-
- m.openTd();
- m.addStyleName(S_ICON_HEADER);
- m.addStyleName("LeftMostCell");
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.append(PatchUtil.C.patchHeaderOld());
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.append(PatchUtil.C.patchHeaderNew());
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_HEADER);
- m.append(Util.C.patchTableColumnComments());
- m.closeTd();
-
- m.closeTr();
- }
-
- private void appendRow(final SafeHtmlBuilder m, final Patch k) {
- m.openTr();
-
- m.openTd();
- m.addStyleName(S_ICON_CELL);
- m.addStyleName("LeftMostCell");
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_CELL);
- m.setAttribute("align", "right");
- if (k != null) {
- final PatchSet.Id psId = k.getKey().getParentKey();
- m.append(Util.M.patchSetHeader(psId.get()));
- } else {
- m.append("Base");
- }
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_CELL);
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_CELL);
- m.nbsp();
- m.closeTd();
-
- m.openTd();
- m.setStyleName(S_DATA_CELL);
- if (k != null && k.getCommentCount() > 0) {
- m.append(Util.M.patchTableComments(k.getCommentCount()));
- } else {
- m.nbsp();
- }
- m.closeTd();
-
- m.closeTr();
- }
-
- private class HistoryRadio extends RadioButton {
- final PatchSet.Id patchSetId;
- final int file;
-
- HistoryRadio(final PatchSet.Id ps, final int f) {
- super(String.valueOf(f));
- sinkEvents(Event.ONCLICK);
- patchSetId = ps;
- file = f;
- }
-
- @Override
- public void onBrowserEvent(final Event event) {
- switch (DOM.eventGetType(event)) {
- case Event.ONCLICK:
- onClick(this);
- break;
- default:
- super.onBrowserEvent(event);
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java b/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java
deleted file mode 100644
index 8768fb74f5..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/PatchBrowserPopup.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.changes.PatchTable;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gwt.event.logical.shared.ResizeEvent;
-import com.google.gwt.event.logical.shared.ResizeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.Window;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.ScrollPanel;
-import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.HidePopupPanelCommand;
-import com.google.gwtexpui.user.client.PluginSafeDialogBox;
-
-class PatchBrowserPopup extends PluginSafeDialogBox implements
- PositionCallback, ResizeHandler {
- private final Patch.Key callerKey;
- private final PatchTable fileList;
- private final ScrollPanel sp;
- private HandlerRegistration regWindowResize;
-
- PatchBrowserPopup(final Patch.Key pk, final PatchTable fl) {
- super(true/* autohide */, false/* modal */);
-
- callerKey = pk;
- fileList = fl;
- sp = new ScrollPanel(fileList);
-
- final FlowPanel body = new FlowPanel();
- body.setStyleName("gerrit-PatchBrowserPopup-Body");
- body.add(sp);
-
- setText(Util.M.patchSetHeader(callerKey.getParentKey().get()));
- setWidget(body);
- addStyleName("gerrit-PatchBrowserPopup");
- }
-
- @Override
- public void setPosition(final int myWidth, int myHeight) {
- final int dLeft = (Window.getClientWidth() - myWidth) >> 1;
- final int cHeight = Window.getClientHeight();
- final int cHeight2 = 2 * cHeight / 3;
- final int sLeft = Window.getScrollLeft();
- final int sTop = Window.getScrollTop();
-
- if (myHeight > cHeight2) {
- sp.setHeight((cHeight2 - 50) + "px");
- myHeight = getOffsetHeight();
- }
- setPopupPosition(sLeft + dLeft, (sTop + cHeight) - (myHeight + 10));
- }
-
- @Override
- public void onResize(final ResizeEvent event) {
- sp.setWidth((Window.getClientWidth() - 60) + "px");
- setPosition(getOffsetWidth(), getOffsetHeight());
- }
-
- @Override
- public void hide() {
- if (regWindowResize != null) {
- regWindowResize.removeHandler();
- regWindowResize = null;
- }
- super.hide();
- }
-
- @Override
- public void show() {
- super.show();
- if (regWindowResize == null) {
- regWindowResize = Window.addResizeHandler(this);
- }
-
- GlobalKey.dialog(this);
- GlobalKey.addApplication(this, new HidePopupPanelCommand(0, 'f', this));
-
- if (!fileList.isLoaded()) {
- fileList.onTableLoaded(new Command() {
- @Override
- public void execute() {
- sp.setHeight("");
- setPosition(getOffsetWidth(), getOffsetHeight());
- fileList.setRegisterKeys(true);
- fileList.movePointerTo(callerKey);
- }
- });
- }
- }
-
- public void open() {
- if (!fileList.isLoaded()) {
- sp.setHeight("22px");
- }
- sp.setWidth((Window.getClientWidth() - 60) + "px");
- setPopupPositionAndShow(this);
- if (fileList.isLoaded()) {
- fileList.setRegisterKeys(true);
- fileList.movePointerTo(callerKey);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchDetailService.java b/src/main/java/com/google/gerrit/client/patches/PatchDetailService.java
deleted file mode 100644
index 446262aec0..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/PatchDetailService.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gerrit.client.data.ApprovalSummarySet;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.PatchScriptSettings;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Patch.Key;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-import java.util.List;
-import java.util.Set;
-
-public interface PatchDetailService extends RemoteJsonService {
- void patchScript(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
- PatchScriptSettings settings, AsyncCallback<PatchScript> callback);
-
- void patchComments(Patch.Key key, PatchSet.Id a, PatchSet.Id b,
- AsyncCallback<CommentDetail> callback);
-
- @SignInRequired
- void saveDraft(PatchLineComment comment,
- AsyncCallback<PatchLineComment> callback);
-
- @SignInRequired
- void deleteDraft(PatchLineComment.Key key, AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void publishComments(PatchSet.Id psid, String message,
- Set<ApprovalCategoryValue.Id> approvals,
- AsyncCallback<VoidResult> callback);
-
- @SignInRequired
- void addReviewers(Change.Id id, List<String> reviewers,
- AsyncCallback<AddReviewerResult> callback);
-
- void userApprovals(Set<Change.Id> cids, Account.Id aid,
- AsyncCallback<ApprovalSummarySet> callback);
-
- void strongestApprovals(Set<Change.Id> cids,
- AsyncCallback<ApprovalSummarySet> callback);
-
- /**
- * Update the reviewed status for the patch.
- */
- @SignInRequired
- void setReviewedByCurrentUser(Key patchKey, boolean reviewed, AsyncCallback<VoidResult> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchScreen.java b/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
deleted file mode 100644
index 22b329a3ac..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.DEFAULT_CONTEXT;
-import static com.google.gerrit.client.reviewdb.AccountGeneralPreferences.WHOLE_FILE_CONTEXT;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.ChangeScreen;
-import com.google.gerrit.client.changes.PatchTable;
-import com.google.gerrit.client.changes.Util;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.PatchScriptSettings;
-import com.google.gerrit.client.data.PatchSetDetail;
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.client.ui.ChangeLink;
-import com.google.gerrit.client.ui.DirectScreenLink;
-import com.google.gerrit.client.ui.Screen;
-import com.google.gwt.event.dom.client.ChangeEvent;
-import com.google.gwt.event.dom.client.ChangeHandler;
-import com.google.gwt.event.dom.client.KeyPressEvent;
-import com.google.gwt.event.logical.shared.CloseEvent;
-import com.google.gwt.event.logical.shared.CloseHandler;
-import com.google.gwt.event.logical.shared.OpenEvent;
-import com.google.gwt.event.logical.shared.OpenHandler;
-import com.google.gwt.event.logical.shared.ValueChangeEvent;
-import com.google.gwt.event.logical.shared.ValueChangeHandler;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.ui.CheckBox;
-import com.google.gwt.user.client.ui.DisclosurePanel;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.Grid;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.Label;
-import com.google.gwt.user.client.ui.ListBox;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.globalkey.client.GlobalKey;
-import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtexpui.globalkey.client.KeyCommandSet;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtjsonrpc.client.VoidResult;
-
-public abstract class PatchScreen extends Screen {
- public static class SideBySide extends PatchScreen {
- public SideBySide(final Patch.Key id, final int patchIndex,
- final PatchTable patchTable) {
- super(id, patchIndex, patchTable);
- }
-
- @Override
- protected SideBySideTable createContentTable() {
- return new SideBySideTable();
- }
-
- @Override
- protected PatchScreen.Type getPatchScreenType() {
- return PatchScreen.Type.SIDE_BY_SIDE;
- }
- }
-
- public static class Unified extends PatchScreen {
- public Unified(final Patch.Key id, final int patchIndex,
- final PatchTable patchTable) {
- super(id, patchIndex, patchTable);
- }
-
- @Override
- protected UnifiedDiffTable createContentTable() {
- return new UnifiedDiffTable();
- }
-
- @Override
- protected PatchScreen.Type getPatchScreenType() {
- return PatchScreen.Type.UNIFIED;
- }
- }
-
- // Which patch set id's are being diff'ed
- private static PatchSet.Id diffSideA = null;
- private static PatchSet.Id diffSideB = null;
- private static Boolean historyOpen = null;
- private static final OpenHandler<DisclosurePanel> cacheOpenState =
- new OpenHandler<DisclosurePanel>() {
- @Override
- public void onOpen(OpenEvent<DisclosurePanel> event) {
- historyOpen = true;
- }
- };
- private static final CloseHandler<DisclosurePanel> cacheCloseState =
- new CloseHandler<DisclosurePanel>() {
- @Override
- public void onClose(CloseEvent<DisclosurePanel> event) {
- historyOpen = false;
- }
- };
-
- // The change id for which the above patch set id's are valid
- private static Change.Id currentChangeId = null;
-
- protected final Patch.Key patchKey;
- protected PatchTable fileList;
- protected PatchSet.Id idSideA;
- protected PatchSet.Id idSideB;
- protected final PatchScriptSettings scriptSettings;
-
- private DisclosurePanel historyPanel;
- private HistoryTable historyTable;
- private FlowPanel contentPanel;
- private Label noDifference;
- private AbstractPatchContentTable contentTable;
-
- private int rpcSequence;
- private PatchScript script;
- private CommentDetail comments;
-
- /** The index of the file we are currently looking at among the fileList */
- private int patchIndex;
-
- /** Keys that cause an action on this screen */
- private KeyCommandSet keysNavigation;
- private HandlerRegistration regNavigation;
-
- /** Link to the screen for the previous file, null if not applicable */
- private DirectScreenLink previousFileLink;
-
- /** Link to the screen for the next file, null if not applicable */
- private DirectScreenLink nextFileLink;
-
- private static final char SHORTCUT_PREVIOUS_FILE = '[';
- private static final char SHORTCUT_NEXT_FILE = ']';
-
- /**
- * How this patch should be displayed in the patch screen.
- */
- public static enum Type {
- UNIFIED, SIDE_BY_SIDE
- }
-
- protected PatchScreen(final Patch.Key id, final int patchIndex,
- final PatchTable patchTable) {
- patchKey = id;
- fileList = patchTable;
-
- // If we have any diff side stored, make sure they are applicable to the
- // current change, discard them otherwise.
- //
- Change.Id thisChangeId = id.getParentKey().getParentKey();
- if (currentChangeId != null && !currentChangeId.equals(thisChangeId)) {
- diffSideA = null;
- diffSideB = null;
- historyOpen = null;
- }
- currentChangeId = thisChangeId;
- idSideA = diffSideA; // null here means we're diff'ing from the Base
- idSideB = diffSideB != null ? diffSideB : id.getParentKey();
- this.patchIndex = patchIndex;
- scriptSettings = new PatchScriptSettings();
-
- initContextLines();
- }
-
- /**
- * Initialize the context lines to the user's preference, or to the default
- * number if the user is not logged in.
- */
- private void initContextLines() {
- if (Gerrit.isSignedIn()) {
- final AccountGeneralPreferences p =
- Gerrit.getUserAccount().getGeneralPreferences();
- scriptSettings.setContext(p.getDefaultContext());
- } else {
- scriptSettings.setContext(DEFAULT_CONTEXT);
- }
- }
-
- @Override
- protected void onInitUI() {
- super.onInitUI();
-
- keysNavigation = new KeyCommandSet(Gerrit.C.sectionNavigation());
- keysNavigation.add(new UpToChangeCommand(0, 'u', PatchUtil.C.upToChange()));
- keysNavigation.add(new FileListCmd(0, 'f', PatchUtil.C.fileList()));
-
- historyTable = new HistoryTable(this);
- historyPanel = new DisclosurePanel(PatchUtil.C.patchHistoryTitle());
- historyPanel.setContent(historyTable);
- historyPanel.setVisible(false);
- // If the user selected a different patch set than the default for either
- // side, expand the history panel
- historyPanel.setOpen(diffSideA != null || diffSideB != null
- || (historyOpen != null && historyOpen));
- historyPanel.addOpenHandler(cacheOpenState);
- historyPanel.addCloseHandler(cacheCloseState);
- add(historyPanel);
- initDisplayControls();
-
- noDifference = new Label(PatchUtil.C.noDifference());
- noDifference.setStyleName("gerrit-PatchNoDifference");
- noDifference.setVisible(false);
-
- contentTable = createContentTable();
- contentTable.fileList = fileList;
-
- add(createNextPrevLinks());
- contentPanel = new FlowPanel();
- contentPanel.setStyleName("gerrit-SideBySideScreen-SideBySideTable");
- contentPanel.add(noDifference);
- contentPanel.add(contentTable);
- add(contentPanel);
- add(createNextPrevLinks());
-
- // This must be done after calling createNextPrevLinks(), which initializes
- // these fields
- if (previousFileLink != null) {
- installLinkShortCut(previousFileLink, SHORTCUT_PREVIOUS_FILE, PatchUtil.C
- .previousFileHelp());
- }
- if (nextFileLink != null) {
- installLinkShortCut(nextFileLink, SHORTCUT_NEXT_FILE, PatchUtil.C
- .nextFileHelp());
- }
- }
-
- private void installLinkShortCut(final DirectScreenLink link, char shortcut,
- String help) {
- keysNavigation.add(new KeyCommand(0, shortcut, help) {
- @Override
- public void onKeyPress(KeyPressEvent event) {
- link.go();
- }
- });
- }
-
- private void initDisplayControls() {
- final Grid displayControls = new Grid(0, 5);
- displayControls.setStyleName("gerrit-PatchScreen-DisplayControls");
- add(displayControls);
-
- createIgnoreWhitespace(displayControls, 0, 0);
- createContext(displayControls, 0, 2);
- }
-
- /**
- * Add the contextual widgets for this patch: "Show full files" and
- * "Keep unreviewed"
- */
- private void createContext(final Grid parent, final int row, final int col) {
- parent.resizeRows(row + 1);
-
- // Show full files
- final CheckBox cb = new CheckBox(PatchUtil.C.showFullFiles());
- cb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- if (event.getValue()) {
- // Show a diff of the full files
- scriptSettings.setContext(WHOLE_FILE_CONTEXT);
- } else {
- // Restore the context lines to the user's preference
- initContextLines();
- }
- refresh(false /* not the first time */);
- }
- });
- parent.setWidget(row, col + 1, cb);
-
- // "Reviewed" check box
- if (Gerrit.isSignedIn()) {
- final CheckBox ku = new CheckBox(PatchUtil.C.reviewed());
- ku.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
- @Override
- public void onValueChange(ValueChangeEvent<Boolean> event) {
- setReviewedByCurrentUser(event.getValue());
- }
- });
- // Checked by default
- ku.setValue(true);
- parent.setWidget(row, col + 2, ku);
- }
-
- }
-
- private void setReviewedByCurrentUser(boolean reviewed) {
- if (fileList != null) {
- fileList.updateReviewedStatus(patchKey, reviewed);
- }
-
- PatchUtil.DETAIL_SVC.setReviewedByCurrentUser(patchKey, reviewed,
- new AsyncCallback<VoidResult>() {
-
- @Override
- public void onFailure(Throwable arg0) {
- // nop
- }
-
- @Override
- public void onSuccess(VoidResult result) {
- // nop
- }
-
- });
- }
-
- private void createIgnoreWhitespace(final Grid parent, final int row,
- final int col) {
- parent.resizeRows(row + 1);
- final ListBox ws = new ListBox();
- ws.addItem(PatchUtil.C.whitespaceIGNORE_NONE(), Whitespace.IGNORE_NONE
- .name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_AT_EOL(),
- Whitespace.IGNORE_SPACE_AT_EOL.name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_SPACE_CHANGE(),
- Whitespace.IGNORE_SPACE_CHANGE.name());
- ws.addItem(PatchUtil.C.whitespaceIGNORE_ALL_SPACE(),
- Whitespace.IGNORE_ALL_SPACE.name());
- ws.addChangeHandler(new ChangeHandler() {
- @Override
- public void onChange(ChangeEvent event) {
- final int sel = ws.getSelectedIndex();
- if (0 <= sel) {
- scriptSettings.setWhitespace(Whitespace.valueOf(ws.getValue(sel)));
- refresh(false /* not the first time */);
- }
- }
- });
- parent.setText(row, col, PatchUtil.C.whitespaceIgnoreLabel());
- parent.setWidget(row, col + 1, ws);
- }
-
- private Widget createNextPrevLinks() {
- final Grid table = new Grid(1, 3);
- final CellFormatter fmt = table.getCellFormatter();
- table.setStyleName("gerrit-SideBySideScreen-LinkTable");
- fmt.setHorizontalAlignment(0, 0, HasHorizontalAlignment.ALIGN_LEFT);
- fmt.setHorizontalAlignment(0, 1, HasHorizontalAlignment.ALIGN_CENTER);
- fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
-
- if (fileList != null) {
- previousFileLink =
- fileList.getPreviousPatchLink(patchIndex, getPatchScreenType());
- table.setWidget(0, 0, previousFileLink);
-
- nextFileLink =
- fileList.getNextPatchLink(patchIndex, getPatchScreenType());
- table.setWidget(0, 2, nextFileLink);
- }
-
- final ChangeLink up =
- new ChangeLink("", patchKey.getParentKey().getParentKey());
- SafeHtml.set(up, SafeHtml.asis(Util.C.upToChangeIconLink()));
- table.setWidget(0, 1, up);
-
- return table;
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- refresh(true);
- }
-
- @Override
- protected void onUnload() {
- if (regNavigation != null) {
- regNavigation.removeHandler();
- regNavigation = null;
- }
- super.onUnload();
- }
-
- @Override
- public void registerKeys() {
- super.registerKeys();
- contentTable.setRegisterKeys(contentTable.isVisible());
- regNavigation = GlobalKey.add(this, keysNavigation);
- }
-
- protected abstract AbstractPatchContentTable createContentTable();
-
- protected abstract PatchScreen.Type getPatchScreenType();
-
- protected void refresh(final boolean isFirst) {
- final int rpcseq = ++rpcSequence;
- script = null;
- comments = null;
-
- // Mark this file reviewed as soon we display the diff screen
- if (Gerrit.isSignedIn() && isFirst) {
- setReviewedByCurrentUser(true /* reviewed */);
- }
-
- PatchUtil.DETAIL_SVC.patchScript(patchKey, idSideA, idSideB,
- scriptSettings, new GerritCallback<PatchScript>() {
- public void onSuccess(final PatchScript result) {
- if (rpcSequence == rpcseq) {
- script = result;
- onResult();
- }
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- if (rpcSequence == rpcseq) {
- super.onFailure(caught);
- }
- }
- });
-
- PatchUtil.DETAIL_SVC.patchComments(patchKey, idSideA, idSideB,
- new GerritCallback<CommentDetail>() {
- public void onSuccess(final CommentDetail result) {
- if (rpcSequence == rpcseq) {
- comments = result;
- onResult();
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- // Ignore no such entity, the patch script RPC above would
- // also notice the problem and report it.
- //
- if (!isNoSuchEntity(caught) && rpcSequence == rpcseq) {
- super.onFailure(caught);
- }
- }
- });
- }
-
- private void onResult() {
- if (script != null && comments != null) {
- final Change.Key cid = script.getChangeId();
- final String path = patchKey.get();
- String fileName = path;
- final int last = fileName.lastIndexOf('/');
- if (last >= 0) {
- fileName = fileName.substring(last + 1);
- }
-
- setWindowTitle(PatchUtil.M.patchWindowTitle(cid.abbreviate(), fileName));
- setPageTitle(PatchUtil.M.patchPageTitle(cid.abbreviate(), path));
-
- historyTable.display(comments.getHistory());
- historyPanel.setVisible(true);
-
- // True if there are differences between the two patch sets
- boolean hasEdits = !script.getEdits().isEmpty();
- // True if this change is a mode change or a pure rename/copy
- boolean hasMeta = !script.getPatchHeader().isEmpty();
-
- boolean hasDifferences = hasEdits || hasMeta;
- boolean pureMetaChange = !hasEdits && hasMeta;
-
- if (contentTable instanceof SideBySideTable && pureMetaChange) {
- // User asked for SideBySide (or a link guessed, wrong) and we can't
- // show a binary or pure-rename change there accurately. Switch to
- // the unified view instead.
- //
- contentTable.removeFromParent();
- contentTable = new UnifiedDiffTable();
- contentTable.fileList = fileList;
- contentPanel.add(contentTable);
- History.newItem(Link.toPatchUnified(patchKey), false);
- }
-
- if (hasDifferences) {
- contentTable.display(patchKey, idSideA, idSideB, script);
- contentTable.display(comments);
- contentTable.finishDisplay();
- }
- showPatch(hasDifferences);
-
- script = null;
- comments = null;
-
- if (!isCurrentView()) {
- display();
- }
- }
- }
-
- private void showPatch(final boolean showPatch) {
- noDifference.setVisible(!showPatch);
- contentTable.setVisible(showPatch);
- contentTable.setRegisterKeys(isCurrentView() && showPatch);
- }
-
- public void setSideA(PatchSet.Id patchSetId) {
- idSideA = patchSetId;
- diffSideA = patchSetId;
- }
-
- public void setSideB(PatchSet.Id patchSetId) {
- idSideB = patchSetId;
- diffSideB = patchSetId;
- }
-
- public class UpToChangeCommand extends KeyCommand {
- public UpToChangeCommand(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- final Change.Id ck = patchKey.getParentKey().getParentKey();
- Gerrit.display(Link.toChange(ck), new ChangeScreen(ck));
- }
- }
-
- public class FileListCmd extends KeyCommand {
- public FileListCmd(int mask, int key, String help) {
- super(mask, key, help);
- }
-
- @Override
- public void onKeyPress(final KeyPressEvent event) {
- if (fileList == null || fileList.isAttached()) {
- final PatchSet.Id psid = patchKey.getParentKey();
- fileList = new PatchTable();
- fileList.setSavePointerId("PatchTable " + psid);
- Util.DETAIL_SVC.patchSetDetail(psid,
- new GerritCallback<PatchSetDetail>() {
- public void onSuccess(final PatchSetDetail result) {
- fileList.display(psid, result.getPatches());
- }
- });
- }
-
- final PatchBrowserPopup p = new PatchBrowserPopup(patchKey, fileList);
- p.open();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/PatchUtil.java b/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
deleted file mode 100644
index aba87965f0..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/PatchUtil.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class PatchUtil {
- public static final PatchConstants C = GWT.create(PatchConstants.class);
- public static final PatchMessages M = GWT.create(PatchMessages.class);
- public static final PatchDetailService DETAIL_SVC;
-
- static {
- DETAIL_SVC = GWT.create(PatchDetailService.class);
- JsonUtil.bind(DETAIL_SVC, "rpc/PatchDetailService");
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java b/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
deleted file mode 100644
index 722772f3cf..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import static com.google.gerrit.client.patches.PatchLine.Type.CONTEXT;
-import static com.google.gerrit.client.patches.PatchLine.Type.DELETE;
-import static com.google.gerrit.client.patches.PatchLine.Type.INSERT;
-import static com.google.gerrit.client.patches.PatchLine.Type.REPLACE;
-
-import com.google.gerrit.client.data.EditList;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.SparseFileContent;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.safehtml.client.PrettyFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-public class SideBySideTable extends AbstractPatchContentTable {
- private static final int COL_A = 2;
- private static final int COL_B = 4;
-
- @Override
- protected void onCellDoubleClick(final int row, int column) {
- if (column > 0 && getRowItem(row) instanceof PatchLine) {
- final PatchLine line = (PatchLine) getRowItem(row);
- final short file = (short) ((column - 1) / 2);
- if (column < (1 + file * 2 + 1)) {
- column++;
- }
- switch (file) {
- case 0:
- createCommentEditor(row + 1, column, line.getLineA(), file);
- break;
- case 1:
- createCommentEditor(row + 1, column, line.getLineB(), file);
- break;
- }
- }
- }
-
- @Override
- protected void onInsertComment(final PatchLine line) {
- final int row = getCurrentRow();
- createCommentEditor(row + 1, 4, line.getLineB(), (short) 1);
- }
-
- @Override
- protected void render(final PatchScript script) {
- final SparseFileContent a = script.getA();
- final SparseFileContent b = script.getB();
- final PrettyFormatter fmtA = PrettyFormatter.newFormatter(formatLanguage);
- final PrettyFormatter fmtB = PrettyFormatter.newFormatter(formatLanguage);
- final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
- final SafeHtmlBuilder nc = new SafeHtmlBuilder();
-
- fmtB.setShowWhiteSpaceErrors(true);
- appendHeader(nc);
- lines.add(null);
-
- int lastB = 0;
- final boolean ignoreWS = script.isIgnoreWhitespace();
- for (final EditList.Hunk hunk : script.getHunks()) {
- if (!hunk.isStartOfFile()) {
- appendSkipLine(nc, hunk.getCurB() - lastB);
- lines.add(null);
- }
-
- while (hunk.next()) {
- if (hunk.isContextLine()) {
- openLine(nc);
- final SafeHtml ctx = fmtA.format(a.get(hunk.getCurA()));
- appendLineText(nc, hunk.getCurA(), CONTEXT, ctx);
- if (ignoreWS && b.contains(hunk.getCurB())) {
- appendLineText(nc, hunk.getCurB(), CONTEXT, b, hunk.getCurB(), fmtB);
- } else {
- appendLineText(nc, hunk.getCurB(), CONTEXT, ctx);
- }
- closeLine(nc);
- hunk.incBoth();
- lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
-
- } else if (hunk.isModifiedLine()) {
- final boolean del = hunk.isDeletedA();
- final boolean ins = hunk.isInsertedB();
- openLine(nc);
-
- if (del) {
- appendLineText(nc, hunk.getCurA(), DELETE, a, hunk.getCurA(), fmtA);
- hunk.incA();
- } else {
- appendLineNone(nc);
- }
-
- if (ins) {
- appendLineText(nc, hunk.getCurB(), INSERT, b, hunk.getCurB(), fmtB);
- hunk.incB();
- } else {
- appendLineNone(nc);
- }
-
- closeLine(nc);
-
- if (del && ins) {
- lines.add(new PatchLine(REPLACE, hunk.getCurA(), hunk.getCurB()));
- } else if (del) {
- lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
- } else if (ins) {
- lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
- }
- }
- }
- lastB = hunk.getCurB();
- }
- if (lastB != b.size()) {
- appendSkipLine(nc, b.size() - lastB);
- }
- resetHtml(nc);
- initScript(script);
-
- for (int row = 0; row < lines.size(); row++) {
- setRowItem(row, lines.get(row));
- }
- }
-
- @Override
- public void display(final CommentDetail cd) {
- if (cd.isEmpty()) {
- return;
- }
- setAccountInfoCache(cd.getAccounts());
-
- for (int row = 0; row < table.getRowCount();) {
- if (getRowItem(row) instanceof PatchLine) {
- final PatchLine pLine = (PatchLine) getRowItem(row);
- final List<PatchLineComment> fora = cd.getForA(pLine.getLineA());
- final List<PatchLineComment> forb = cd.getForB(pLine.getLineB());
- row++;
-
- final Iterator<PatchLineComment> ai = fora.iterator();
- final Iterator<PatchLineComment> bi = forb.iterator();
- while (ai.hasNext() && bi.hasNext()) {
- final PatchLineComment ac = ai.next();
- final PatchLineComment bc = bi.next();
- insertRow(row);
- bindComment(row, COL_A, ac, !ai.hasNext());
- bindComment(row, COL_B, bc, !bi.hasNext());
- row++;
- }
-
- row = finish(ai, row, COL_A);
- row = finish(bi, row, COL_B);
- } else {
- row++;
- }
- }
- }
-
- @Override
- protected void insertRow(final int row) {
- super.insertRow(row);
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, COL_A - 1, "LineNumber");
- fmt.addStyleName(row, COL_A, "DiffText");
- fmt.addStyleName(row, COL_B - 1, "LineNumber");
- fmt.addStyleName(row, COL_B, "DiffText");
- }
-
- private int finish(final Iterator<PatchLineComment> i, int row, final int col) {
- while (i.hasNext()) {
- final PatchLineComment c = i.next();
- insertRow(row);
- bindComment(row, col, c, !i.hasNext());
- row++;
- }
- return row;
- }
-
- private void appendHeader(final SafeHtmlBuilder m) {
- m.openTr();
-
- m.openTd();
- m.addStyleName(S_ICON_CELL);
- m.addStyleName("FileColumnHeader");
- m.closeTd();
-
- m.openTd();
- m.addStyleName("FileColumnHeader");
- m.addStyleName("LineNumber");
- m.closeTd();
-
- m.openTd();
- m.setStyleName("FileColumnHeader");
- m.setAttribute("width", "50%");
- m.append(PatchUtil.C.patchHeaderOld());
- m.closeTd();
-
- m.openTd();
- m.addStyleName("FileColumnHeader");
- m.addStyleName("LineNumber");
- m.closeTd();
-
- m.openTd();
- m.setStyleName("FileColumnHeader");
- m.setAttribute("width", "50%");
- m.append(PatchUtil.C.patchHeaderNew());
- m.closeTd();
-
- m.closeTr();
- }
-
- private void appendSkipLine(final SafeHtmlBuilder m, final int skipCnt) {
- m.openTr();
-
- m.openTd();
- m.setStyleName(S_ICON_CELL);
- m.closeTd();
-
- m.openTd();
- m.setStyleName("SkipLine");
- m.setAttribute("colspan", 4);
- m.append(PatchUtil.M.patchSkipRegion(skipCnt));
- m.closeTd();
- m.closeTr();
- }
-
- private void openLine(final SafeHtmlBuilder m) {
- m.openTr();
- m.setAttribute("valign", "top");
-
- m.openTd();
- m.setStyleName(S_ICON_CELL);
- m.closeTd();
- }
-
- private SafeHtml appendLineText(final SafeHtmlBuilder m,
- final int lineNumberMinusOne, final PatchLine.Type type,
- final SparseFileContent src, final int i, final PrettyFormatter dst) {
- final SafeHtml lineHtml = dst.format(src.get(i));
- appendLineText(m, lineNumberMinusOne, type, lineHtml);
- return lineHtml;
- }
-
- private void appendLineText(final SafeHtmlBuilder m,
- final int lineNumberMinusOne, final PatchLine.Type type,
- final SafeHtml lineHtml) {
- m.openTd();
- m.setStyleName("LineNumber");
- m.append(lineNumberMinusOne + 1);
- m.closeTd();
-
- m.openTd();
- m.addStyleName("FileLine");
- m.addStyleName("FileLine-" + type.name());
- m.append(lineHtml);
- m.closeTd();
- }
-
- private void appendLineNone(final SafeHtmlBuilder m) {
- m.openTd();
- m.setStyleName("LineNumber");
- m.closeTd();
-
- m.openTd();
- m.addStyleName("FileLine");
- m.addStyleName("FileLineNone");
- m.closeTd();
- }
-
- private void closeLine(final SafeHtmlBuilder m) {
- m.closeTr();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java b/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
deleted file mode 100644
index 19c0bd1904..0000000000
--- a/src/main/java/com/google/gerrit/client/patches/UnifiedDiffTable.java
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.patches;
-
-import static com.google.gerrit.client.patches.PatchLine.Type.CONTEXT;
-import static com.google.gerrit.client.patches.PatchLine.Type.DELETE;
-import static com.google.gerrit.client.patches.PatchLine.Type.INSERT;
-
-import com.google.gerrit.client.data.EditList;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.SparseFileContent;
-import com.google.gerrit.client.data.EditList.Hunk;
-import com.google.gerrit.client.data.PatchScript.DisplayMethod;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.safehtml.client.PrettyFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtorm.client.KeyUtil;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
-public class UnifiedDiffTable extends AbstractPatchContentTable {
- private static final int PC = 3;
- private static final Comparator<PatchLineComment> BY_DATE =
- new Comparator<PatchLineComment>() {
- public int compare(final PatchLineComment o1, final PatchLineComment o2) {
- return o1.getWrittenOn().compareTo(o2.getWrittenOn());
- }
- };
-
- @Override
- protected void onCellDoubleClick(final int row, final int column) {
- if (getRowItem(row) instanceof PatchLine) {
- final PatchLine pl = (PatchLine) getRowItem(row);
- switch (pl.getType()) {
- case DELETE:
- case CONTEXT:
- createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
- break;
- case INSERT:
- createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
- break;
- }
- }
- }
-
- @Override
- protected void onInsertComment(final PatchLine pl) {
- final int row = getCurrentRow();
- switch (pl.getType()) {
- case DELETE:
- case CONTEXT:
- createCommentEditor(row + 1, PC, pl.getLineA(), (short) 0);
- break;
- case INSERT:
- createCommentEditor(row + 1, PC, pl.getLineB(), (short) 1);
- break;
- }
- }
-
- private void appendImgTag(SafeHtmlBuilder nc, String url) {
- nc.openElement("img");
- nc.setAttribute("src", url);
- nc.closeElement("img");
- }
-
- @Override
- protected void render(final PatchScript script) {
- final SparseFileContent a = script.getA();
- final SparseFileContent b = script.getB();
- final SafeHtmlBuilder nc = new SafeHtmlBuilder();
- final PrettyFormatter fmtA = PrettyFormatter.newFormatter(formatLanguage);
- final PrettyFormatter fmtB = PrettyFormatter.newFormatter(formatLanguage);
-
- fmtB.setShowWhiteSpaceErrors(true);
-
- // Display the patch header
- for (final String line : script.getPatchHeader()) {
- appendFileHeader(nc, line);
- }
-
- if (script.getDisplayMethodA() == DisplayMethod.IMG
- || script.getDisplayMethodB() == DisplayMethod.IMG) {
- final String rawBase = GWT.getHostPageBaseURL() + "cat/";
-
- nc.openTr();
- nc.setAttribute("valign", "center");
- nc.setAttribute("align", "center");
-
- nc.openTd();
- nc.nbsp();
- nc.closeTd();
-
- nc.openTd();
- nc.nbsp();
- nc.closeTd();
-
- nc.openTd();
- nc.nbsp();
- nc.closeTd();
-
- nc.openTd();
- if (script.getDisplayMethodA() == DisplayMethod.IMG) {
- if (idSideA == null) {
- appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^1");
- } else {
- Patch.Key k = new Patch.Key(idSideA, patchKey.get());
- appendImgTag(nc, rawBase + KeyUtil.encode(k.toString()) + "^0");
- }
- }
- if (script.getDisplayMethodB() == DisplayMethod.IMG) {
- appendImgTag(nc, rawBase + KeyUtil.encode(patchKey.toString()) + "^0");
- }
- nc.closeTd();
-
- nc.closeTr();
- }
-
- final ArrayList<PatchLine> lines = new ArrayList<PatchLine>();
- for (final EditList.Hunk hunk : script.getHunks()) {
- appendHunkHeader(nc, hunk);
- while (hunk.next()) {
- if (hunk.isContextLine()) {
- openLine(nc);
- appendLineNumber(nc, hunk.getCurA());
- appendLineNumber(nc, hunk.getCurB());
- appendLineText(nc, CONTEXT, a, hunk.getCurA(), fmtA, fmtB);
- closeLine(nc);
- hunk.incBoth();
- lines.add(new PatchLine(CONTEXT, hunk.getCurA(), hunk.getCurB()));
-
- } else if (hunk.isDeletedA()) {
- openLine(nc);
- appendLineNumber(nc, hunk.getCurA());
- padLineNumber(nc);
- appendLineText(nc, DELETE, a, hunk.getCurA(), fmtA, fmtB);
- closeLine(nc);
- hunk.incA();
- lines.add(new PatchLine(DELETE, hunk.getCurA(), 0));
- if (a.size() == hunk.getCurA() && a.isMissingNewlineAtEnd())
- appendNoLF(nc);
-
- } else if (hunk.isInsertedB()) {
- openLine(nc);
- padLineNumber(nc);
- appendLineNumber(nc, hunk.getCurB());
- appendLineText(nc, INSERT, b, hunk.getCurB(), fmtA, fmtB);
- closeLine(nc);
- hunk.incB();
- lines.add(new PatchLine(INSERT, 0, hunk.getCurB()));
- if (b.size() == hunk.getCurB() && b.isMissingNewlineAtEnd())
- appendNoLF(nc);
- }
- }
- }
- resetHtml(nc);
- initScript(script);
-
- int row = script.getPatchHeader().size();
- final CellFormatter fmt = table.getCellFormatter();
- final Iterator<PatchLine> iLine = lines.iterator();
- while (iLine.hasNext()) {
- final PatchLine l = iLine.next();
- final String n = "DiffText-" + l.getType().name();
- while (!fmt.getStyleName(row, PC).contains(n)) {
- row++;
- }
- setRowItem(row++, l);
- }
- }
-
- @Override
- public void display(final CommentDetail cd) {
- if (cd.isEmpty()) {
- return;
- }
- setAccountInfoCache(cd.getAccounts());
-
- final ArrayList<PatchLineComment> all = new ArrayList<PatchLineComment>();
- for (int row = 0; row < table.getRowCount();) {
- if (getRowItem(row) instanceof PatchLine) {
- final PatchLine pLine = (PatchLine) getRowItem(row);
- final List<PatchLineComment> fora = cd.getForA(pLine.getLineA());
- final List<PatchLineComment> forb = cd.getForB(pLine.getLineB());
- row++;
-
- if (!fora.isEmpty() && !forb.isEmpty()) {
- all.clear();
- all.addAll(fora);
- all.addAll(forb);
- Collections.sort(all, BY_DATE);
- row = insert(all, row);
-
- } else if (!fora.isEmpty()) {
- row = insert(fora, row);
-
- } else if (!forb.isEmpty()) {
- row = insert(forb, row);
- }
- } else {
- row++;
- }
- }
- }
-
-
- @Override
- protected void insertRow(final int row) {
- super.insertRow(row);
- final CellFormatter fmt = table.getCellFormatter();
- fmt.addStyleName(row, PC - 2, "LineNumber");
- fmt.addStyleName(row, PC - 1, "LineNumber");
- fmt.addStyleName(row, PC, "DiffText");
- }
-
- private int insert(final List<PatchLineComment> in, int row) {
- for (Iterator<PatchLineComment> ci = in.iterator(); ci.hasNext();) {
- final PatchLineComment c = ci.next();
- insertRow(row);
- bindComment(row, PC, c, !ci.hasNext());
- row++;
- }
- return row;
- }
-
- private void appendFileHeader(final SafeHtmlBuilder m, final String line) {
- openLine(m);
- padLineNumber(m);
- padLineNumber(m);
-
- m.openTd();
- m.addStyleName("DiffText");
- m.addStyleName("DiffText-FILE_HEADER");
- m.append(line);
- m.closeTd();
- closeLine(m);
- }
-
- private void appendHunkHeader(final SafeHtmlBuilder m, final Hunk hunk) {
- openLine(m);
- padLineNumber(m);
- padLineNumber(m);
-
- m.openTd();
- m.addStyleName("DiffText");
- m.addStyleName("DiffText-HUNK_HEADER");
- m.append("@@ -");
- appendRange(m, hunk.getCurA() + 1, hunk.getEndA() - hunk.getCurA());
- m.append(" +");
- appendRange(m, hunk.getCurB() + 1, hunk.getEndB() - hunk.getCurB());
- m.append(" @@");
- m.closeTd();
-
- closeLine(m);
- }
-
- private void appendRange(final SafeHtmlBuilder m, final int begin,
- final int cnt) {
- switch (cnt) {
- case 0:
- m.append(begin - 1);
- m.append(",0");
- break;
-
- case 1:
- m.append(begin);
- break;
-
- default:
- m.append(begin);
- m.append(',');
- m.append(cnt);
- break;
- }
- }
-
- private void appendLineText(final SafeHtmlBuilder m,
- final PatchLine.Type type, final SparseFileContent src, final int i,
- final PrettyFormatter fmtA, final PrettyFormatter fmtB) {
- final String text = src.get(i);
- m.openTd();
- m.addStyleName("DiffText");
- m.addStyleName("DiffText-" + type.name());
- switch (type) {
- case CONTEXT:
- m.nbsp();
- m.append(fmtA.format(text));
- fmtB.update(text);
- break;
- case DELETE:
- m.append("-");
- m.append(fmtA.format(text));
- break;
- case INSERT:
- m.append("+");
- m.append(fmtB.format(text));
- break;
- }
- m.closeTd();
- }
-
- private void appendNoLF(final SafeHtmlBuilder m) {
- openLine(m);
- padLineNumber(m);
- padLineNumber(m);
- m.openTd();
- m.addStyleName("DiffText");
- m.addStyleName("DiffText-NO_LF");
- m.append("\\ No newline at end of file");
- m.closeTd();
- closeLine(m);
- }
-
- private void openLine(final SafeHtmlBuilder m) {
- m.openTr();
- m.setAttribute("valign", "top");
- m.openTd();
- m.setStyleName(S_ICON_CELL);
- m.closeTd();
- }
-
- private void closeLine(final SafeHtmlBuilder m) {
- m.closeTr();
- }
-
- private void padLineNumber(final SafeHtmlBuilder m) {
- m.openTd();
- m.setStyleName("LineNumber");
- m.closeTd();
- }
-
- private void appendLineNumber(final SafeHtmlBuilder m, final int idx) {
- m.openTd();
- m.setStyleName("LineNumber");
- m.append(idx + 1);
- m.closeTd();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AbstractAgreement.java b/src/main/java/com/google/gerrit/client/reviewdb/AbstractAgreement.java
deleted file mode 100644
index 3364fedf62..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AbstractAgreement.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import java.sql.Timestamp;
-
-/** Base for {@link AccountAgreement} or {@link AccountGroupAgreement}. */
-public interface AbstractAgreement {
- public static enum Status {
- NEW('n'),
-
- VERIFIED('V'),
-
- REJECTED('R');
-
- private final char code;
-
- private Status(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static Status forCode(final char c) {
- for (final Status s : Status.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- public ContributorAgreement.Id getAgreementId();
-
- public Timestamp getAcceptedOn();
-
- public Status getStatus();
-
- public Timestamp getReviewedOn();
-
- public Account.Id getReviewedBy();
-
- public String getReviewComments();
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Account.java b/src/main/java/com/google/gerrit/client/reviewdb/Account.java
deleted file mode 100644
index 95af60ed67..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/Account.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-import java.sql.Timestamp;
-
-/**
- * Information about a single user.
- * <p>
- * A user may have multiple identities they can use to login to Gerrit (see
- * {@link AccountExternalId}), but in such cases they always map back to a
- * single Account entity.
- *<p>
- * Entities "owned" by an Account (that is, their primary key contains the
- * {@link Account.Id} key as part of their key structure):
- * <ul>
- * <li>{@link AccountAgreement}: any record of the user's acceptance of a
- * predefined {@link ContributorAgreement}. Multiple records indicate
- * potentially multiple agreements, especially if agreements must be retired and
- * replaced with new agreements.</li>
- *
- * <li>{@link AccountExternalId}: OpenID identities and email addresses known to
- * be registered to this user. Multiple records can exist when the user has more
- * than one public identity, such as a work and a personal email address.</li>
- *
- * <li>{@link AccountGroupMember}: membership of the user in a specific human
- * managed {@link AccountGroup}. Multiple records can exist when the user is a
- * member of more than one group.</li>
- *
- * <li>{@link AccountProjectWatch}: user's email settings related to a specific
- * {@link Project}. One record per project the user is interested in tracking.</li>
- *
- * <li>{@link AccountSshKey}: user's public SSH keys, for authentication through
- * the internal SSH daemon. One record per SSH key uploaded by the user, keys
- * are checked in random order until a match is found.</li>
- *
- * <li>{@link StarredChange}: user has starred the change, tracking
- * notifications of updates on that change, or just book-marking it for faster
- * future reference. One record per starred change.</li>
- * </ul>
- */
-public final class Account {
- public static enum FieldName {
- FULL_NAME, SSH_USER_NAME, REGISTER_NEW_EMAIL;
- }
-
- public static final String SSH_USER_NAME_PATTERN_FIRST = "[a-zA-Z]";
- public static final String SSH_USER_NAME_PATTERN_REST = "[a-zA-Z0-9._-]";
- public static final String SSH_USER_NAME_PATTERN_LAST = "[a-zA-Z0-9]";
-
- /** Regular expression that {@link #sshUserName} must match. */
- public static final String SSH_USER_NAME_PATTERN = "^" + //
- SSH_USER_NAME_PATTERN_FIRST + //
- SSH_USER_NAME_PATTERN_REST + "*" + //
- SSH_USER_NAME_PATTERN_LAST + //
- "$";
-
- /** Key local to Gerrit to identify a user. */
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected int id;
-
- protected Id() {
- }
-
- public Id(final int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
-
- /** Parse an Account.Id out of a string representation. */
- public static Id parse(final String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
- }
- }
-
- @Column
- protected Id accountId;
-
- /** Date and time the user registered with the review server. */
- @Column
- protected Timestamp registeredOn;
-
- /** Full name of the user ("Given-name Surname" style). */
- @Column(notNull = false)
- protected String fullName;
-
- /** Email address the user prefers to be contacted through. */
- @Column(notNull = false)
- protected String preferredEmail;
-
- /** Username to authenticate as through SSH connections. */
- @Column(notNull = false)
- protected String sshUserName;
-
- /** When did the user last give us contact information? Null if never. */
- @Column(notNull = false)
- protected Timestamp contactFiledOn;
-
- /** This user's preferences */
- @Column(name = Column.NONE)
- protected AccountGeneralPreferences generalPreferences;
-
- protected Account() {
- }
-
- /**
- * Create a new account.
- *
- * @param newId unique id, see {@link ReviewDb#nextAccountId()}.
- */
- public Account(final Account.Id newId) {
- accountId = newId;
- registeredOn = new Timestamp(System.currentTimeMillis());
-
- generalPreferences = new AccountGeneralPreferences();
- generalPreferences.resetToDefaults();
- }
-
- /** Get local id of this account, to link with in other entities */
- public Account.Id getId() {
- return accountId;
- }
-
- /** Get the full name of the user ("Given-name Surname" style). */
- public String getFullName() {
- return fullName;
- }
-
- /** Set the full name of the user ("Given-name Surname" style). */
- public void setFullName(final String name) {
- fullName = name;
- }
-
- /** Email address the user prefers to be contacted through. */
- public String getPreferredEmail() {
- return preferredEmail;
- }
-
- /** Set the email address the user prefers to be contacted through. */
- public void setPreferredEmail(final String addr) {
- preferredEmail = addr;
- }
-
- /** Get the name the user logins as through SSH. */
- public String getSshUserName() {
- return sshUserName;
- }
-
- /** Set the name the user logins as through SSH. */
- public void setSshUserName(final String name) {
- sshUserName = name;
- }
-
- /** Get the date and time the user first registered. */
- public Timestamp getRegisteredOn() {
- return registeredOn;
- }
-
- public AccountGeneralPreferences getGeneralPreferences() {
- return generalPreferences;
- }
-
- public void setGeneralPreferences(final AccountGeneralPreferences p) {
- generalPreferences = p;
- }
-
- public boolean isContactFiled() {
- return contactFiledOn != null;
- }
-
- public Timestamp getContactFiledOn() {
- return contactFiledOn;
- }
-
- public void setContactFiled() {
- contactFiledOn = new Timestamp(System.currentTimeMillis());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountAccess.java
deleted file mode 100644
index 688185254f..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountAccess.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.SecondaryKey;
-
-/** Access interface for {@link Account}. */
-public interface AccountAccess extends Access<Account, Account.Id> {
- /** Locate an account by our locally generated identity. */
- @PrimaryKey("accountId")
- Account get(Account.Id key) throws OrmException;
-
- @Query("WHERE preferredEmail = ? LIMIT 2")
- ResultSet<Account> byPreferredEmail(String email) throws OrmException;
-
- @SecondaryKey("sshUserName")
- Account bySshUserName(String userName) throws OrmException;
-
- @Query("WHERE fullName = ? LIMIT 2")
- ResultSet<Account> byFullName(String name) throws OrmException;
-
- @Query("WHERE fullName >= ? AND fullName <= ? ORDER BY fullName LIMIT ?")
- ResultSet<Account> suggestByFullName(String nameA, String nameB, int limit)
- throws OrmException;
-
- @Query("WHERE preferredEmail >= ? AND preferredEmail <= ? ORDER BY preferredEmail LIMIT ?")
- ResultSet<Account> suggestByPreferredEmail(String nameA, String nameB,
- int limit) throws OrmException;
-
- @Query("WHERE sshUserName >= ? AND sshUserName <= ? ORDER BY sshUserName LIMIT ?")
- ResultSet<Account> suggestBySshUserName(String nameA, String nameB, int limit)
- throws OrmException;
-
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreement.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreement.java
deleted file mode 100644
index 75f52d3e55..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreement.java
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-import java.sql.Timestamp;
-
-/** Electronic acceptance of a {@link ContributorAgreement} by {@link Account} */
-public final class AccountAgreement implements AbstractAgreement {
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected ContributorAgreement.Id claId;
-
- protected Key() {
- accountId = new Account.Id();
- claId = new ContributorAgreement.Id();
- }
-
- public Key(final Account.Id account, final ContributorAgreement.Id cla) {
- this.accountId = account;
- this.claId = cla;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {claId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- @Column
- protected Timestamp acceptedOn;
-
- @Column
- protected char status;
-
- @Column(notNull = false)
- protected Account.Id reviewedBy;
-
- @Column(notNull = false)
- protected Timestamp reviewedOn;
-
- @Column(notNull = false, length = Integer.MAX_VALUE)
- protected String reviewComments;
-
- protected AccountAgreement() {
- }
-
- public AccountAgreement(final AccountAgreement.Key k) {
- key = k;
- acceptedOn = new Timestamp(System.currentTimeMillis());
- status = Status.NEW.getCode();
- }
-
- public AccountAgreement.Key getKey() {
- return key;
- }
-
- public ContributorAgreement.Id getAgreementId() {
- return key.claId;
- }
-
- public Timestamp getAcceptedOn() {
- return acceptedOn;
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public Timestamp getReviewedOn() {
- return reviewedOn;
- }
-
- public Account.Id getReviewedBy() {
- return reviewedBy;
- }
-
- public String getReviewComments() {
- return reviewComments;
- }
-
- public void setReviewComments(final String s) {
- reviewComments = s;
- }
-
- public void review(final Status newStatus, final Account.Id by) {
- status = newStatus.getCode();
- reviewedBy = by;
- reviewedOn = new Timestamp(System.currentTimeMillis());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreementAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreementAccess.java
deleted file mode 100644
index 7628d7a8ea..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountAgreementAccess.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountAgreementAccess extends
- Access<AccountAgreement, AccountAgreement.Key> {
- @PrimaryKey("key")
- AccountAgreement get(AccountAgreement.Key key) throws OrmException;
-
- @Query("WHERE key.accountId = ? ORDER BY acceptedOn DESC")
- ResultSet<AccountAgreement> byAccount(Account.Id id) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalId.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalId.java
deleted file mode 100644
index c7ad3e0a84..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalId.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-import java.sql.Timestamp;
-import java.util.Collection;
-
-/** Association of an external account identifier to a local {@link Account}. */
-public final class AccountExternalId {
- public static final String SCHEME_GERRIT = "gerrit:";
- public static final String SCHEME_MAILTO = "mailto:";
- public static final String LEGACY_GAE = "Google Account ";
-
- public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected String externalId;
-
- protected Key() {
- }
-
- public Key(final String e) {
- externalId = e;
- }
-
- @Override
- public String get() {
- return externalId;
- }
-
- @Override
- protected void set(String newValue) {
- externalId = newValue;
- }
- }
-
- /**
- * Select the most recently used identity from a list of identities.
- *
- * @param all all known identities
- * @return most recently used login identity; null if none matches.
- */
- public static AccountExternalId mostRecent(Collection<AccountExternalId> all) {
- AccountExternalId mostRecent = null;
- for (final AccountExternalId e : all) {
- final Timestamp lastUsed = e.getLastUsedOn();
- if (lastUsed == null) {
- // Identities without logins have never been used, so
- // they can't be the most recent.
- //
- continue;
- }
-
- if (e.isScheme(SCHEME_MAILTO)) {
- // Don't ever consider an email address as a "recent login"
- //
- continue;
- }
-
- if (mostRecent == null
- || lastUsed.getTime() > mostRecent.getLastUsedOn().getTime()) {
- mostRecent = e;
- }
- }
- return mostRecent;
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- @Column
- protected Account.Id accountId;
-
- @Column(notNull = false)
- protected String emailAddress;
-
- @Column(notNull = false)
- protected Timestamp lastUsedOn;
-
- /** <i>computed value</i> is this identity trusted by the site administrator? */
- protected boolean trusted;
-
- protected AccountExternalId() {
- }
-
- /**
- * Create a new binding to an external identity.
- *
- * @param who the account this binds to.
- * @param k the binding key.
- */
- public AccountExternalId(final Account.Id who, final AccountExternalId.Key k) {
- accountId = who;
- key = k;
- }
-
- public AccountExternalId.Key getKey() {
- return key;
- }
-
- /** Get local id of this account, to link with in other entities */
- public Account.Id getAccountId() {
- return accountId;
- }
-
- public String getExternalId() {
- return key.externalId;
- }
-
- public String getEmailAddress() {
- return emailAddress;
- }
-
- public void setEmailAddress(final String e) {
- emailAddress = e;
- }
-
- public Timestamp getLastUsedOn() {
- return lastUsedOn;
- }
-
- public void setLastUsedOn() {
- lastUsedOn = new Timestamp(System.currentTimeMillis());
- }
-
- public boolean isScheme(final String scheme) {
- final String id = getExternalId();
- return id != null && id.startsWith(scheme);
- }
-
- public String getSchemeRest(final String scheme) {
- return isScheme(scheme) ? getExternalId().substring(scheme.length()) : null;
- }
-
- public boolean isTrusted() {
- return trusted;
- }
-
- public void setTrusted(final boolean t) {
- trusted = t;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalIdAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalIdAccess.java
deleted file mode 100644
index 4b65724e02..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountExternalIdAccess.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountExternalIdAccess extends
- Access<AccountExternalId, AccountExternalId.Key> {
- @PrimaryKey("key")
- AccountExternalId get(AccountExternalId.Key key) throws OrmException;
-
- @Query("WHERE accountId = ?")
- ResultSet<AccountExternalId> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE accountId = ? AND emailAddress = ?")
- ResultSet<AccountExternalId> byAccountEmail(Account.Id id, String email)
- throws OrmException;
-
- @Query("WHERE emailAddress = ?")
- ResultSet<AccountExternalId> byEmailAddress(String email) throws OrmException;
-
- @Query("WHERE emailAddress >= ? AND emailAddress <= ? ORDER BY emailAddress LIMIT ?")
- ResultSet<AccountExternalId> suggestByEmailAddress(String emailA,
- String emailB, int limit) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGeneralPreferences.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGeneralPreferences.java
deleted file mode 100644
index 90c4a8a049..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGeneralPreferences.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-
-/** Preferences about a single user. */
-public final class AccountGeneralPreferences {
- /** Default number of lines of context. */
- public static final short DEFAULT_CONTEXT = 10;
-
- /** Context setting to display the entire file. */
- public static final short WHOLE_FILE_CONTEXT = -1;
-
- /** Typical valid choices for the default context setting. */
- public static final short[] CONTEXT_CHOICES =
- {3, 10, 25, 50, 75, 100, WHOLE_FILE_CONTEXT};
-
- /** Default number of items to display per page. */
- public static final short DEFAULT_PAGESIZE = 25;
-
- /** Valid choices for the page size. */
- public static final short[] PAGESIZE_CHOICES = {10, 25, 50, 100};
-
- /** Default number of lines of context when viewing a patch. */
- @Column
- protected short defaultContext;
-
- /** Number of changes to show in a screen. */
- @Column
- protected short maximumPageSize;
-
- /** Should the site header be displayed when logged in ? */
- @Column
- protected boolean showSiteHeader;
-
- /** Should the Flash helper movie be used to copy text to the clipboard? */
- @Column
- protected boolean useFlashClipboard;
-
- public AccountGeneralPreferences() {
- }
-
- /** Get the default number of lines of context when viewing a patch. */
- public short getDefaultContext() {
- return defaultContext;
- }
-
- /** Set the number of lines of context when viewing a patch. */
- public void setDefaultContext(final short s) {
- defaultContext = s;
- }
-
- public short getMaximumPageSize() {
- return maximumPageSize;
- }
-
- public void setMaximumPageSize(final short s) {
- maximumPageSize = s;
- }
-
- public boolean isShowSiteHeader() {
- return showSiteHeader;
- }
-
- public void setShowSiteHeader(final boolean b) {
- showSiteHeader = b;
- }
-
- public boolean isUseFlashClipboard() {
- return useFlashClipboard;
- }
-
- public void setUseFlashClipboard(final boolean b) {
- useFlashClipboard = b;
- }
-
- public void resetToDefaults() {
- defaultContext = DEFAULT_CONTEXT;
- maximumPageSize = DEFAULT_PAGESIZE;
- showSiteHeader = true;
- useFlashClipboard = true;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroup.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroup.java
deleted file mode 100644
index eb38ef212f..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroup.java
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.StringKey;
-
-/** Named group of one or more accounts, typically used for access controls. */
-public final class AccountGroup {
- /** Group name key */
- public static class NameKey extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(length = 40)
- protected String name;
-
- protected NameKey() {
- }
-
- public NameKey(final String n) {
- name = n;
- }
-
- @Override
- public String get() {
- return name;
- }
-
- @Override
- protected void set(String newValue) {
- name = newValue;
- }
- }
-
- /** Distinguished name, within organization directory server. */
- public static class ExternalNameKey extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected String name;
-
- protected ExternalNameKey() {
- }
-
- public ExternalNameKey(final String n) {
- name = n;
- }
-
- @Override
- public String get() {
- return name;
- }
-
- @Override
- protected void set(String newValue) {
- name = newValue;
- }
- }
-
- /** Synthetic key to link to within the database */
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected int id;
-
- protected Id() {
- }
-
- public Id(final int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
-
- /** Parse an AccountGroup.Id out of a string representation. */
- public static Id parse(final String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
- }
- }
-
- public static enum Type {
- /**
- * System defined and managed group, e.g. anonymous users.
- * <p>
- * These groups must be explicitly named by {@link SystemConfig} and are
- * specially handled throughout the code. In UI contexts their membership is
- * not displayed. When computing effective group membership for any given
- * user account, these groups are automatically handled using specialized
- * branch conditions.
- */
- SYSTEM,
-
- /**
- * Group defined within our database.
- * <p>
- * An internal group has its membership fully enumerated in the database.
- * The membership can be viewed and edited through the web UI by any user
- * who is a member of the owner group. These groups are not treated special
- * in the code.
- */
- INTERNAL,
-
- /**
- * Group defined by external LDAP database.
- * <p>
- * A group whose membership is determined by the LDAP directory that we
- * connect to for user and group information. In UI contexts the membership
- * of the group is not displayed, as it may be exceedingly large, or might
- * contain users who have never logged into this server before (and thus
- * have no matching account record). Adding or removing users from an LDAP
- * group requires making edits through the LDAP directory, and cannot be
- * done through our UI.
- */
- LDAP;
- }
-
- /** Unique name of this group within the system. */
- @Column
- protected NameKey name;
-
- /** Unique identity, to link entities as {@link #name} can change. */
- @Column
- protected Id groupId;
-
- /**
- * Identity of the group whose members can manage this group.
- * <p>
- * This can be a self-reference to indicate the group's members manage itself.
- */
- @Column
- protected Id ownerGroupId;
-
- /** A textual description of the group's purpose. */
- @Column(length = Integer.MAX_VALUE, notNull = false)
- protected String description;
-
- /** Is the membership managed by some external means? */
- @Column(length = 8)
- protected String groupType;
-
- /** Distinguished name in the directory server. */
- @Column(notNull = false)
- protected ExternalNameKey externalName;
-
- protected AccountGroup() {
- }
-
- public AccountGroup(final AccountGroup.NameKey newName,
- final AccountGroup.Id newId) {
- name = newName;
- groupId = newId;
- ownerGroupId = groupId;
- setType(Type.INTERNAL);
- }
-
- public AccountGroup.Id getId() {
- return groupId;
- }
-
- public String getName() {
- return name.get();
- }
-
- public AccountGroup.NameKey getNameKey() {
- return name;
- }
-
- public void setNameKey(final AccountGroup.NameKey nameKey) {
- name = nameKey;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(final String d) {
- description = d;
- }
-
- public AccountGroup.Id getOwnerGroupId() {
- return ownerGroupId;
- }
-
- public void setOwnerGroupId(final AccountGroup.Id id) {
- ownerGroupId = id;
- }
-
- public Type getType() {
- return Type.valueOf(groupType);
- }
-
- public void setType(final Type t) {
- groupType = t.name();
- }
-
- public ExternalNameKey getExternalNameKey() {
- return externalName;
- }
-
- public void setExternalNameKey(final ExternalNameKey k) {
- externalName = k;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAccess.java
deleted file mode 100644
index 308a15e1e0..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAccess.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.SecondaryKey;
-
-public interface AccountGroupAccess extends
- Access<AccountGroup, AccountGroup.Id> {
- @PrimaryKey("groupId")
- AccountGroup get(AccountGroup.Id id) throws OrmException;
-
- @SecondaryKey("name")
- AccountGroup get(AccountGroup.NameKey name) throws OrmException;
-
- @SecondaryKey("externalName")
- AccountGroup get(AccountGroup.ExternalNameKey name) throws OrmException;
-
- @Query("ORDER BY name")
- ResultSet<AccountGroup> all() throws OrmException;
-
- @Query("WHERE ownerGroupId = ?")
- ResultSet<AccountGroup> ownedByGroup(AccountGroup.Id groupId)
- throws OrmException;
-
- @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
- ResultSet<AccountGroup> suggestByName(String nameA, String nameB, int limit)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreement.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreement.java
deleted file mode 100644
index 4a5d979eb0..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreement.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-import java.sql.Timestamp;
-
-/**
- * Acceptance of a {@link ContributorAgreement} by an {@link AccountGroup}.
- */
-public final class AccountGroupAgreement implements AbstractAgreement {
- public static class Key extends CompoundKey<AccountGroup.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected AccountGroup.Id groupId;
-
- @Column
- protected ContributorAgreement.Id claId;
-
- protected Key() {
- groupId = new AccountGroup.Id();
- claId = new ContributorAgreement.Id();
- }
-
- public Key(final AccountGroup.Id group, final ContributorAgreement.Id cla) {
- this.groupId = group;
- this.claId = cla;
- }
-
- @Override
- public AccountGroup.Id getParentKey() {
- return groupId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {claId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- @Column
- protected Timestamp acceptedOn;
-
- @Column
- protected char status;
-
- @Column(notNull = false)
- protected Account.Id reviewedBy;
-
- @Column(notNull = false)
- protected Timestamp reviewedOn;
-
- @Column(notNull = false, length = Integer.MAX_VALUE)
- protected String reviewComments;
-
- protected AccountGroupAgreement() {
- }
-
- public AccountGroupAgreement(final AccountGroupAgreement.Key k) {
- key = k;
- acceptedOn = new Timestamp(System.currentTimeMillis());
- status = Status.NEW.getCode();
- }
-
- public AccountGroupAgreement.Key getKey() {
- return key;
- }
-
- public ContributorAgreement.Id getAgreementId() {
- return key.claId;
- }
-
- public Timestamp getAcceptedOn() {
- return acceptedOn;
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public Timestamp getReviewedOn() {
- return reviewedOn;
- }
-
- public Account.Id getReviewedBy() {
- return reviewedBy;
- }
-
- public String getReviewComments() {
- return reviewComments;
- }
-
- public void setReviewComments(final String s) {
- reviewComments = s;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreementAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreementAccess.java
deleted file mode 100644
index d3efaa3964..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupAgreementAccess.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountGroupAgreementAccess extends
- Access<AccountGroupAgreement, AccountGroupAgreement.Key> {
- @PrimaryKey("key")
- AccountGroupAgreement get(AccountGroupAgreement.Key key) throws OrmException;
-
- @Query("WHERE key.groupId = ? ORDER BY acceptedOn DESC")
- ResultSet<AccountGroupAgreement> byGroup(AccountGroup.Id id)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMember.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMember.java
deleted file mode 100644
index 835988ea21..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMember.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-/** Membership of an {@link Account} in an {@link AccountGroup}. */
-public final class AccountGroupMember {
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected AccountGroup.Id groupId;
-
- protected Key() {
- accountId = new Account.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(final Account.Id a, final AccountGroup.Id g) {
- accountId = a;
- groupId = g;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return groupId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {groupId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- protected AccountGroupMember() {
- }
-
- public AccountGroupMember(final AccountGroupMember.Key k) {
- key = k;
- }
-
- public AccountGroupMember.Key getKey() {
- return key;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return key.groupId;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAccess.java
deleted file mode 100644
index 8a18919170..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAccess.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountGroupMemberAccess extends
- Access<AccountGroupMember, AccountGroupMember.Key> {
- @PrimaryKey("key")
- AccountGroupMember get(AccountGroupMember.Key key) throws OrmException;
-
- @Query("WHERE key.accountId = ?")
- ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE key.groupId = ?")
- ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAudit.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAudit.java
deleted file mode 100644
index 7fb448a593..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAudit.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-import java.sql.Timestamp;
-
-/** Membership of an {@link Account} in an {@link AccountGroup}. */
-public final class AccountGroupMemberAudit {
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected AccountGroup.Id groupId;
-
- @Column
- protected Timestamp addedOn;
-
- protected Key() {
- accountId = new Account.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(final Account.Id a, final AccountGroup.Id g, final Timestamp t) {
- accountId = a;
- groupId = g;
- addedOn = t;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {groupId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- @Column
- protected Account.Id addedBy;
-
- @Column(notNull = false)
- protected Account.Id removedBy;
-
- @Column(notNull = false)
- protected Timestamp removedOn;
-
- protected AccountGroupMemberAudit() {
- }
-
- public AccountGroupMemberAudit(final AccountGroupMember m,
- final Account.Id adder) {
- final Account.Id who = m.getAccountId();
- final AccountGroup.Id group = m.getAccountGroupId();
- key = new AccountGroupMemberAudit.Key(who, group, now());
- addedBy = adder;
- }
-
- public AccountGroupMemberAudit.Key getKey() {
- return key;
- }
-
- public boolean isActive() {
- return removedOn == null;
- }
-
- public void removed(final Account.Id deleter) {
- removedBy = deleter;
- removedOn = now();
- }
-
- public void removedLegacy() {
- removedBy = addedBy;
- removedOn = key.addedOn;
- }
-
- private static Timestamp now() {
- return new Timestamp(System.currentTimeMillis());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAuditAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAuditAccess.java
deleted file mode 100644
index 19e13aff11..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountGroupMemberAuditAccess.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountGroupMemberAuditAccess extends
- Access<AccountGroupMemberAudit, AccountGroupMemberAudit.Key> {
- @PrimaryKey("key")
- AccountGroupMemberAudit get(AccountGroupMemberAudit.Key key)
- throws OrmException;
-
- @Query("WHERE key.groupId = ? AND key.accountId = ?")
- ResultSet<AccountGroupMemberAudit> byGroupAccount(AccountGroup.Id groupId,
- Account.Id accountId) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReview.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReview.java
deleted file mode 100644
index cde95a17bd..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReview.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-/**
- * An entity that keeps track of what user reviewed what patches.
- */
-public final class AccountPatchReview {
-
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column(name = Column.NONE)
- protected Patch.Key patchKey;
-
- protected Key() {
- accountId = new Account.Id();
- patchKey = new Patch.Key();
- }
-
- public Key(final Patch.Key p, final Account.Id a) {
- patchKey = p;
- accountId = a;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- public Patch.Key getPatchKey() {
- return patchKey;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {patchKey};
- }
- }
-
- @Column(name = Column.NONE)
- protected AccountPatchReview.Key key;
-
- protected AccountPatchReview() {
- }
-
- public AccountPatchReview(final Patch.Key k, final Account.Id a) {
- key = new AccountPatchReview.Key(k, a);
- }
-
- public AccountPatchReview.Key getKey() {
- return key;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReviewAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReviewAccess.java
deleted file mode 100644
index 43a05e3814..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountPatchReviewAccess.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountPatchReviewAccess
- extends Access<AccountPatchReview, AccountPatchReview.Key> {
- @PrimaryKey("key")
- AccountPatchReview get(AccountPatchReview.Key id) throws OrmException;
-
- @Query("WHERE key.accountId = ? AND key.patchKey.patchSetId = ?")
- ResultSet<AccountPatchReview> byReviewer(Account.Id who, PatchSet.Id ps) throws OrmException;
-
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java
deleted file mode 100644
index 9a0be5d13e..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatch.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-/** An {@link Account} interested in a {@link Project}. */
-public final class AccountProjectWatch {
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected Project.NameKey projectName;
-
- protected Key() {
- accountId = new Account.Id();
- projectName = new Project.NameKey();
- }
-
- public Key(final Account.Id a, final Project.NameKey g) {
- accountId = a;
- projectName = g;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {projectName};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- /** Automatically send email notifications of new changes? */
- @Column
- protected boolean notifyNewChanges;
-
- /** Automatically receive comments published to this project */
- @Column
- protected boolean notifyAllComments;
-
- /** Automatically receive changes submitted to this project */
- @Column
- protected boolean notifySubmittedChanges;
-
- protected AccountProjectWatch() {
- }
-
- public AccountProjectWatch(final AccountProjectWatch.Key k) {
- key = k;
- }
-
- public AccountProjectWatch.Key getKey() {
- return key;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public Project.NameKey getProjectNameKey() {
- return key.projectName;
- }
-
- public boolean isNotifyNewChanges() {
- return notifyNewChanges;
- }
-
- public void setNotifyNewChanges(final boolean a) {
- notifyNewChanges = a;
- }
-
- public boolean isNotifyAllComments() {
- return notifyAllComments;
- }
-
- public void setNotifyAllComments(final boolean a) {
- notifyAllComments = a;
- }
-
- public boolean isNotifySubmittedChanges() {
- return notifySubmittedChanges;
- }
-
- public void setNotifySubmittedChanges(final boolean a) {
- notifySubmittedChanges = a;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java
deleted file mode 100644
index 2aaeab71ae..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountProjectWatchAccess.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountProjectWatchAccess extends
- Access<AccountProjectWatch, AccountProjectWatch.Key> {
- @PrimaryKey("key")
- AccountProjectWatch get(AccountProjectWatch.Key key) throws OrmException;
-
- @Query("WHERE key.accountId = ?")
- ResultSet<AccountProjectWatch> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE notifyNewChanges = true AND key.projectName = ?")
- ResultSet<AccountProjectWatch> notifyNewChanges(Project.NameKey name)
- throws OrmException;
-
- @Query("WHERE notifyAllComments = true AND key.projectName = ?")
- ResultSet<AccountProjectWatch> notifyAllComments(Project.NameKey name)
- throws OrmException;
-
- @Query("WHERE notifySubmittedChanges = true AND key.projectName = ?")
- ResultSet<AccountProjectWatch> notifySubmittedChanges(Project.NameKey name)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKey.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKey.java
deleted file mode 100644
index cece1b2f12..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKey.java
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-import java.sql.Timestamp;
-
-/** An SSH key approved for use by an {@link Account}. */
-public final class AccountSshKey {
- public static class Id extends IntKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected int seq;
-
- protected Id() {
- accountId = new Account.Id();
- }
-
- public Id(final Account.Id a, final int s) {
- accountId = a;
- seq = s;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- @Override
- public int get() {
- return seq;
- }
-
- @Override
- protected void set(int newValue) {
- seq = newValue;
- }
- }
-
- @Column(name = Column.NONE)
- protected AccountSshKey.Id id;
-
- @Column(length = Integer.MAX_VALUE)
- protected String sshPublicKey;
-
- @Column
- protected Timestamp storedOn;
-
- @Column
- protected boolean valid;
-
- @Column(notNull = false)
- protected Timestamp lastUsedOn;
-
- protected AccountSshKey() {
- }
-
- public AccountSshKey(final AccountSshKey.Id i, final String pub) {
- id = i;
- sshPublicKey = pub;
- storedOn = new Timestamp(System.currentTimeMillis());
- valid = true; // We can assume it is fine.
- }
-
- public Account.Id getAccount() {
- return id.accountId;
- }
-
- public AccountSshKey.Id getKey() {
- return id;
- }
-
- public String getSshPublicKey() {
- return sshPublicKey;
- }
-
- public String getAlgorithm() {
- final String s = getSshPublicKey();
- if (s == null || s.length() == 0) {
- return "none";
- }
-
- final String[] parts = s.split(" ");
- if (parts.length < 1) {
- return "none";
- }
- return parts[0];
- }
-
- public String getEncodedKey() {
- final String s = getSshPublicKey();
- if (s == null || s.length() == 0) {
- return null;
- }
-
- final String[] parts = s.split(" ");
- if (parts.length < 2) {
- return null;
- }
- return parts[1];
- }
-
- public String getComment() {
- final String s = getSshPublicKey();
- if (s == null || s.length() == 0) {
- return "";
- }
-
- final String[] parts = s.split(" ", 3);
- if (parts.length < 3) {
- return "";
- }
- return parts[2];
- }
-
- public Timestamp getStoredOn() {
- return storedOn;
- }
-
- public boolean isValid() {
- return valid;
- }
-
- public void setInvalid() {
- valid = false;
- }
-
- public Timestamp getLastUsedOn() {
- return lastUsedOn;
- }
-
- public void setLastUsedOn() {
- lastUsedOn = new Timestamp(System.currentTimeMillis());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKeyAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKeyAccess.java
deleted file mode 100644
index 10f4935002..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AccountSshKeyAccess.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface AccountSshKeyAccess extends
- Access<AccountSshKey, AccountSshKey.Id> {
- @PrimaryKey("id")
- AccountSshKey get(AccountSshKey.Id id) throws OrmException;
-
- @Query("WHERE id.accountId = ? ORDER BY storedOn DESC")
- ResultSet<AccountSshKey> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE id.accountId = ? AND valid = true ORDER BY storedOn DESC")
- ResultSet<AccountSshKey> valid(Account.Id id) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategory.java b/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategory.java
deleted file mode 100644
index 2aa5df6714..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategory.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.Key;
-import com.google.gwtorm.client.StringKey;
-
-/** Types of approvals that can be associated with a {@link Change}. */
-public final class ApprovalCategory {
- /** Id of the special "Submit" action (and category). */
- public static final ApprovalCategory.Id SUBMIT =
- new ApprovalCategory.Id("SUBM");
-
- /** Id of the special "Read" action (and category). */
- public static final ApprovalCategory.Id READ =
- new ApprovalCategory.Id("READ");
-
- /** Id of the special "Own" category; manages a project. */
- public static final ApprovalCategory.Id OWN = new ApprovalCategory.Id("OWN");
-
- /** Id of the special "Push Annotated Tag" action (and category). */
- public static final ApprovalCategory.Id PUSH_TAG =
- new ApprovalCategory.Id("pTAG");
- public static final short PUSH_TAG_SIGNED = 1;
- public static final short PUSH_TAG_ANNOTATED = 2;
- public static final short PUSH_TAG_ANY = 3;
-
- /** Id of the special "Push Branch" action (and category). */
- public static final ApprovalCategory.Id PUSH_HEAD =
- new ApprovalCategory.Id("pHD");
- public static final short PUSH_HEAD_UPDATE = 1;
- public static final short PUSH_HEAD_CREATE = 2;
- public static final short PUSH_HEAD_REPLACE = 3;
-
- public static class Id extends StringKey<Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(length = 4)
- protected String id;
-
- protected Id() {
- }
-
- public Id(final String a) {
- id = a;
- }
-
- @Override
- public String get() {
- return id;
- }
-
- @Override
- protected void set(String newValue) {
- id = newValue;
- }
-
- /** True if the right can inherit from the magical "-- All Projects --". */
- public boolean canInheritFromWildProject() {
- if (OWN.equals(this)) {
- return false;
- }
- return true;
- }
- }
-
- /** Internal short unique identifier for this category. */
- @Column
- protected Id categoryId;
-
- /** Unique name for this category, shown in the web interface to users. */
- @Column(length = 20)
- protected String name;
-
- /** Abbreviated form of {@link #name} for display in very wide tables. */
- @Column(length = 4, notNull = false)
- protected String abbreviatedName;
-
- /**
- * Order of this category within the Approvals table when presented.
- * <p>
- * If < 0 (e.g. -1) this category is not shown in the Approvals table but is
- * instead considered to be an action that the user might be able to perform,
- * e.g. "Submit".
- * <p>
- * If >= 0 this category is shown in the Approvals table, sorted along with
- * its siblings by <code>position, name</code>.
- */
- @Column
- protected short position;
-
- /** Identity of the function used to aggregate the category's value. */
- @Column
- protected String functionName;
-
- /** If set, the minimum score is copied during patch set replacement. */
- @Column
- protected boolean copyMinScore;
-
- protected ApprovalCategory() {
- }
-
- public ApprovalCategory(final ApprovalCategory.Id id, final String name) {
- this.categoryId = id;
- this.name = name;
- this.functionName = "MaxWithBlock";
- }
-
- public ApprovalCategory.Id getId() {
- return categoryId;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(final String n) {
- name = n;
- }
-
- public String getAbbreviatedName() {
- return abbreviatedName;
- }
-
- public void setAbbreviatedName(final String n) {
- abbreviatedName = n;
- }
-
- public short getPosition() {
- return position;
- }
-
- public void setPosition(final short p) {
- position = p;
- }
-
- public boolean isAction() {
- return position < 0;
- }
-
- public String getFunctionName() {
- return functionName;
- }
-
- public void setFunctionName(final String name) {
- functionName = name;
- }
-
- public boolean isCopyMinScore() {
- return copyMinScore;
- }
-
- public void setCopyMinScore(final boolean copy) {
- copyMinScore = copy;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryAccess.java
deleted file mode 100644
index 45df69933e..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryAccess.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.SecondaryKey;
-
-public interface ApprovalCategoryAccess extends
- Access<ApprovalCategory, ApprovalCategory.Id> {
- @PrimaryKey("categoryId")
- ApprovalCategory get(ApprovalCategory.Id id) throws OrmException;
-
- @SecondaryKey("name")
- ApprovalCategory byName(String name) throws OrmException;
-
- @Query("ORDER BY position, name")
- ResultSet<ApprovalCategory> all() throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValue.java b/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValue.java
deleted file mode 100644
index 0f9a8f55ce..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValue.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.ShortKey;
-
-/** Valid value for a {@link ApprovalCategory}. */
-public final class ApprovalCategoryValue {
- public static class Id extends ShortKey<ApprovalCategory.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected ApprovalCategory.Id categoryId;
-
- @Column
- protected short value;
-
- protected Id() {
- categoryId = new ApprovalCategory.Id();
- }
-
- public Id(final ApprovalCategory.Id cat, final short v) {
- categoryId = cat;
- value = v;
- }
-
- @Override
- public ApprovalCategory.Id getParentKey() {
- return categoryId;
- }
-
- @Override
- public short get() {
- return value;
- }
-
- @Override
- protected void set(short newValue) {
- value = newValue;
- }
- }
-
- @Column(name = Column.NONE)
- protected Id key;
-
- @Column(length = 50)
- protected String name;
-
- protected ApprovalCategoryValue() {
- }
-
- public ApprovalCategoryValue(final ApprovalCategoryValue.Id id,
- final String name) {
- this.key = id;
- this.name = name;
- }
-
- public ApprovalCategoryValue.Id getId() {
- return key;
- }
-
- public ApprovalCategory.Id getCategoryId() {
- return key.categoryId;
- }
-
- public short getValue() {
- return key.value;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(final String n) {
- name = n;
- }
-
- public String formatValue() {
- if (getValue() < 0) {
- return Short.toString(getValue());
- } else if (getValue() == 0) {
- return " 0";
- } else {
- return "+" + Short.toString(getValue());
- }
- }
-
- public String format() {
- final StringBuilder m = new StringBuilder();
- m.append(formatValue());
- m.append(' ');
- m.append(getName());
- return m.toString();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValueAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValueAccess.java
deleted file mode 100644
index 9b8a64c15c..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ApprovalCategoryValueAccess.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface ApprovalCategoryValueAccess extends
- Access<ApprovalCategoryValue, ApprovalCategoryValue.Id> {
- @PrimaryKey("key")
- ApprovalCategoryValue get(ApprovalCategoryValue.Id key) throws OrmException;
-
- @Query("WHERE key.categoryId = ? ORDER BY key.value")
- ResultSet<ApprovalCategoryValue> byCategory(ApprovalCategory.Id id)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/AuthType.java b/src/main/java/com/google/gerrit/client/reviewdb/AuthType.java
deleted file mode 100644
index 055218f857..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/AuthType.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-public enum AuthType {
- /** Login relies upon the OpenID standard: {@link "http://openid.net/"} */
- OPENID,
-
- /**
- * Login relies upon the container/web server security.
- * <p>
- * The container or web server must populate an HTTP header with a unique name
- * for the current user. Gerrit will implicitly trust the value of this header
- * to supply the unique identity.
- */
- HTTP,
-
- /**
- * Login relies upon the container/web server security, but also uses LDAP.
- * <p>
- * Like {@link #HTTP}, the container or web server must populate an HTTP
- * header with a unique name for the current user. Gerrit will implicitly
- * trust the value of this header to supply the unique identity.
- * <p>
- * In addition to trusting the HTTP headers, Gerrit will obtain basic user
- * registration (name and email) from LDAP, and some group memberships.
- */
- HTTP_LDAP,
-
- /**
- * Login collects username and password through a web form, and binds to LDAP.
- * <p>
- * Unlike {@link #HTTP_LDAP}, Gerrit presents a sign-in dialog to the user and
- * makes the connection to the LDAP server on their behalf.
- */
- LDAP,
-
- /** Development mode to enable becoming anyone you want. */
- DEVELOPMENT_BECOME_ANY_ACCOUNT;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Branch.java b/src/main/java/com/google/gerrit/client/reviewdb/Branch.java
deleted file mode 100644
index 06b18e6f2a..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/Branch.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** Line of development within a {@link Project}. */
-public final class Branch {
- public static final String R_HEADS = "refs/heads/";
- public static final String R_REFS = "refs/";
-
- /** Branch name key */
- public static class NameKey extends StringKey<Project.NameKey> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Project.NameKey projectName;
-
- @Column
- protected String branchName;
-
- protected NameKey() {
- projectName = new Project.NameKey();
- }
-
- public NameKey(final Project.NameKey proj, final String n) {
- projectName = proj;
- branchName = n;
- }
-
- @Override
- public String get() {
- return branchName;
- }
-
- @Override
- protected void set(String newValue) {
- branchName = newValue;
- }
-
- @Override
- public Project.NameKey getParentKey() {
- return projectName;
- }
-
- public String getShortName() {
- final String n = get();
-
- // Git style branches will tend to start with "refs/heads/".
- //
- if (n.startsWith(R_HEADS)) {
- return n.substring(R_HEADS.length());
- }
-
- return n;
- }
- }
-
- protected NameKey name;
- protected RevId revision;
-
- protected Branch() {
- }
-
- public Branch(final Branch.NameKey newName) {
- name = newName;
- }
-
- public Branch.NameKey getNameKey() {
- return name;
- }
-
- public String getName() {
- return name.get();
- }
-
- public String getShortName() {
- return name.getShortName();
- }
-
- public RevId getRevision() {
- return revision;
- }
-
- public void setRevision(final RevId id) {
- revision = id;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Change.java b/src/main/java/com/google/gerrit/client/reviewdb/Change.java
deleted file mode 100644
index cda6d22f56..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/Change.java
+++ /dev/null
@@ -1,451 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.RowVersion;
-import com.google.gwtorm.client.StringKey;
-
-import java.sql.Timestamp;
-
-/**
- * A change proposed to be merged into a {@link Branch}.
- * <p>
- * The data graph rooted below a Change can be quite complex:
- *
- * <pre>
- * {@link Change}
- * |
- * +- {@link ChangeMessage}: &quot;cover letter&quot; or general comment.
- * |
- * +- {@link PatchSet}: a single variant of this change.
- * |
- * +- {@link PatchSetApproval}: a +/- vote on the change's current state.
- * |
- * +- {@link PatchSetAncestor}: parents of this change's commit.
- * |
- * +- {@link PatchLineComment}: comment about a specific line
- * </pre>
- * <p>
- * <h5>PatchSets</h5>
- * <p>
- * Every change has at least one PatchSet. A change starts out with one
- * PatchSet, the initial proposal put forth by the change owner. This
- * {@link Account} is usually also listed as the author and committer in the
- * PatchSetInfo.
- * <p>
- * The {@link PatchSetAncestor} entities are a mirror of the Git commit
- * metadata, providing access to the information without needing direct
- * accessing Git. These entities are actually legacy artifacts from Gerrit 1.x
- * and could be removed, replaced by direct RevCommit access.
- * <p>
- * Each PatchSet contains zero or more Patch records, detailing the file paths
- * impacted by the change (otherwise known as, the file paths the author
- * added/deleted/modified). Sometimes a merge commit can contain zero patches,
- * if the merge has no conflicts, or has no impact other than to cut off a line
- * of development.
- * <p>
- * Each PatchLineComment is a draft or a published comment about a single line
- * of the associated file. These are the inline comment entities created by
- * users as they perform a review.
- * <p>
- * When additional PatchSets appear under a change, these PatchSets reference
- * <i>replacement</i> commits; alternative commits that could be made to the
- * project instead of the original commit referenced by the first PatchSet.
- * <p>
- * A change has at most one current PatchSet. The current PatchSet is updated
- * when a new replacement PatchSet is uploaded. When a change is submitted, the
- * current patch set is what is merged into the destination branch.
- * <p>
- * <h5>ChangeMessage</h5>
- * <p>
- * The ChangeMessage entity is a general free-form comment about the whole
- * change, rather than PatchLineComment's file and line specific context. The
- * ChangeMessage appears at the start of any email generated by Gerrit, and is
- * shown on the change overview page, rather than in a file-specific context.
- * Users often use this entity to describe general remarks about the overall
- * concept proposed by the change.
- * <p>
- * <h5>PatchSetApproval</h5>
- * <p>
- * PatchSetApproval entities exist to fill in the <i>cells</i> of the approvals
- * table in the web UI. That is, a single PatchSetApproval record's key is the
- * tuple {@code (PatchSet,Account,ApprovalCategory)}. Each PatchSetApproval
- * carries with it a small score value, typically within the range -2..+2.
- * <p>
- * If an Account has created only PatchSetApprovals with a score value of 0, the
- * Change shows in their dashboard, and they are said to be CC'd (carbon copied)
- * on the Change, but are not a direct reviewer. This often happens when an
- * account was specified at upload time with the {@code --cc} command line flag,
- * or have published comments, but left the approval scores at 0 ("No Score").
- * <p>
- * If an Account has one or more PatchSetApprovals with a score != 0, the Change
- * shows in their dashboard, and they are said to be an active reviewer. Such
- * individuals are highlighted when notice of a replacement patch set is sent,
- * or when notice of the change submission occurs.
- */
-public final class Change {
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected int id;
-
- protected Id() {
- }
-
- public Id(final int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
-
- /** Parse a Change.Id out of a string representation. */
- public static Id parse(final String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
- }
- }
-
- /** Globally unique identification of this change. */
- public static class Key extends StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(length = 60)
- protected String id;
-
- protected Key() {
- }
-
- public Key(final String id) {
- this.id = id;
- }
-
- @Override
- public String get() {
- return id;
- }
-
- @Override
- protected void set(String newValue) {
- id = newValue;
- }
-
- /** Construct a key that is after all keys prefixed by this key. */
- public Key max() {
- final StringBuilder revEnd = new StringBuilder(get().length() + 1);
- revEnd.append(get());
- revEnd.append('\u9fa5');
- return new Key(revEnd.toString());
- }
-
- /** Obtain a shorter version of this key string, using a leading prefix. */
- public String abbreviate() {
- final String s = get();
- return s.substring(0, Math.min(s.length(), 9));
- }
-
- /** Parse a Change.Key out of a string representation. */
- public static Key parse(final String str) {
- final Key r = new Key();
- r.fromString(str);
- return r;
- }
- }
-
- /** Minimum database status constant for an open change. */
- private static final char MIN_OPEN = 'a';
- /** Database constant for {@link Status#NEW}. */
- protected static final char STATUS_NEW = 'n';
- /** Database constant for {@link Status#SUBMITTED}. */
- protected static final char STATUS_SUBMITTED = 's';
- /** Maximum database status constant for an open change. */
- private static final char MAX_OPEN = 'z';
-
- /** Database constant for {@link Status#MERGED}. */
- protected static final char STATUS_MERGED = 'M';
-
- /**
- * Current state within the basic workflow of the change.
- *
- * <p>
- * Within the database, lower case codes ('a'..'z') indicate a change that is
- * still open, and that can be modified/refined further, while upper case
- * codes ('A'..'Z') indicate a change that is closed and cannot be further
- * modified.
- * */
- public static enum Status {
- /**
- * Change is open and pending review, or review is in progress.
- *
- * <p>
- * This is the default state assigned to a change when it is first created
- * in the database. A change stays in the NEW state throughout its review
- * cycle, until the change is submitted or abandoned.
- *
- * <p>
- * Changes in the NEW state can be moved to:
- * <ul>
- * <li>{@link #SUBMITTED} - when the Submit Patch Set action is used;
- * <li>{@link #ABANDONED} - when the Abandon action is used.
- * </ul>
- */
- NEW(STATUS_NEW),
-
- /**
- * Change is open, but has been submitted to the merge queue.
- *
- * <p>
- * A change enters the SUBMITTED state when an authorized user presses the
- * "submit" action through the web UI, requesting that Gerrit merge the
- * change's current patch set into the destination branch.
- *
- * <p>
- * Typically a change resides in the SUBMITTED for only a brief sub-second
- * period while the merge queue fires and the destination branch is updated.
- * However, if a dependency commit (a {@link PatchSetAncestor}, directly or
- * transitively) is not yet merged into the branch, the change will hang in
- * the SUBMITTED state indefinately.
- *
- * <p>
- * Changes in the SUBMITTED state can be moved to:
- * <ul>
- * <li>{@link #NEW} - when a replacement patch set is supplied, OR when a
- * merge conflict is detected;
- * <li>{@link #MERGED} - when the change has been successfully merged into
- * the destination branch;
- * <li>{@link #ABANDONED} - when the Abandon action is used.
- * </ul>
- */
- SUBMITTED(STATUS_SUBMITTED),
-
- /**
- * Change is closed, and submitted to its destination branch.
- *
- * <p>
- * Once a change has been merged, it cannot be further modified by adding a
- * replacement patch set. Draft comments however may be published,
- * supporting a post-submit review.
- */
- MERGED(STATUS_MERGED),
-
- /**
- * Change is closed, but was not submitted to its destination branch.
- *
- * <p>
- * Once a change has been abandoned, it cannot be further modified by adding
- * a replacement patch set, and it cannot be merged. Draft comments however
- * may be published, permitting reviewers to send constructive feedback.
- */
- ABANDONED('A');
-
- private final char code;
- private final boolean closed;
-
- private Status(final char c) {
- code = c;
- closed = !(MIN_OPEN <= c && c <= MAX_OPEN);
- }
-
- public char getCode() {
- return code;
- }
-
- public boolean isOpen() {
- return !closed;
- }
-
- public boolean isClosed() {
- return closed;
- }
-
- public static Status forCode(final char c) {
- for (final Status s : Status.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- /** Locally assigned unique identifier of the change */
- @Column
- protected Id changeId;
-
- /** Globally assigned unique identifier of the change */
- @Column
- protected Key changeKey;
-
- /** optimistic locking */
- @Column
- @RowVersion
- protected int rowVersion;
-
- /** When this change was first introduced into the database. */
- @Column
- protected Timestamp createdOn;
-
- /**
- * When was a meaningful modification last made to this record's data
- * <p>
- * Note, this update timestamp includes its children.
- */
- @Column
- protected Timestamp lastUpdatedOn;
-
- /** A {@link #lastUpdatedOn} ASC,{@link #changeId} ASC for sorting. */
- @Column(length = 16)
- protected String sortKey;
-
- @Column(name = "owner_account_id")
- protected Account.Id owner;
-
- /** The branch (and project) this change merges into. */
- @Column
- protected Branch.NameKey dest;
-
- /** Is the change currently open? Set to {@link #status}.isOpen(). */
- @Column
- protected boolean open;
-
- /** Current state code; see {@link Status}. */
- @Column
- protected char status;
-
- /** The total number of {@link PatchSet} children in this Change. */
- @Column
- protected int nbrPatchSets;
-
- /** The current patch set. */
- @Column
- protected int currentPatchSetId;
-
- /** Subject from the current patch set. */
- @Column
- protected String subject;
-
- protected Change() {
- }
-
- public Change(final Change.Key newKey, final Change.Id newId,
- final Account.Id ownedBy, final Branch.NameKey forBranch) {
- changeKey = newKey;
- changeId = newId;
- createdOn = new Timestamp(System.currentTimeMillis());
- lastUpdatedOn = createdOn;
- owner = ownedBy;
- dest = forBranch;
- setStatus(Status.NEW);
- }
-
- /** Legacy 32 bit integer identity for a change. */
- @Deprecated
- public Change.Id getId() {
- return changeId;
- }
-
- /** Legacy 32 bit integer identity for a change. */
- @Deprecated
- public int getChangeId() {
- return changeId.get();
- }
-
- /** The Change-Id tag out of the initial commit, or a natural key. */
- public Change.Key getKey() {
- return changeKey;
- }
-
- public void setKey(final Change.Key k) {
- changeKey = k;
- }
-
- public Timestamp getCreatedOn() {
- return createdOn;
- }
-
- public Timestamp getLastUpdatedOn() {
- return lastUpdatedOn;
- }
-
- public void resetLastUpdatedOn() {
- lastUpdatedOn = new Timestamp(System.currentTimeMillis());
- }
-
- public String getSortKey() {
- return sortKey;
- }
-
- public void setSortKey(final String newSortKey) {
- sortKey = newSortKey;
- }
-
- public Account.Id getOwner() {
- return owner;
- }
-
- public Branch.NameKey getDest() {
- return dest;
- }
-
- public Project.NameKey getProject() {
- return dest.getParentKey();
- }
-
- public String getSubject() {
- return subject;
- }
-
- /** Get the id of the most current {@link PatchSet} in this change. */
- public PatchSet.Id currentPatchSetId() {
- if (currentPatchSetId > 0) {
- return new PatchSet.Id(changeId, currentPatchSetId);
- }
- return null;
- }
-
- public void setCurrentPatchSet(final PatchSetInfo ps) {
- currentPatchSetId = ps.getKey().get();
- subject = ps.getSubject();
- }
-
- /**
- * Allocate a new PatchSet id within this change.
- * <p>
- * <b>Note: This makes the change dirty. Call update() after.</b>
- */
- public PatchSet.Id newPatchSetId() {
- return new PatchSet.Id(changeId, ++nbrPatchSets);
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public void setStatus(final Status newStatus) {
- open = newStatus.isOpen();
- status = newStatus.getCode();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ChangeAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ChangeAccess.java
deleted file mode 100644
index 97bed628d1..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ChangeAccess.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface ChangeAccess extends Access<Change, Change.Id> {
- @PrimaryKey("changeId")
- Change get(Change.Id id) throws OrmException;
-
- @Query("WHERE changeKey = ?")
- ResultSet<Change> byKey(Change.Key key) throws OrmException;
-
- @Query("WHERE changeKey >= ? AND changeKey <= ?")
- ResultSet<Change> byKeyRange(Change.Key reva, Change.Key revb)
- throws OrmException;
-
- @Query("WHERE dest.projectName = ? AND changeKey = ?")
- ResultSet<Change> byProjectKey(Project.NameKey p, Change.Key key)
- throws OrmException;
-
- @Query("WHERE owner = ? AND open = true ORDER BY createdOn, changeId")
- ResultSet<Change> byOwnerOpen(Account.Id id) throws OrmException;
-
- @Query("WHERE owner = ? AND open = false ORDER BY lastUpdatedOn DESC LIMIT 5")
- ResultSet<Change> byOwnerClosed(Account.Id id) throws OrmException;
-
- @Query("WHERE owner = ? AND open = false ORDER BY lastUpdatedOn")
- ResultSet<Change> byOwnerClosedAll(Account.Id id) throws OrmException;
-
- @Query("WHERE dest = ? AND status = '" + Change.STATUS_SUBMITTED
- + "' ORDER BY lastUpdatedOn")
- ResultSet<Change> submitted(Branch.NameKey dest) throws OrmException;
-
- @Query("WHERE status = '" + Change.STATUS_SUBMITTED + "'")
- ResultSet<Change> allSubmitted() throws OrmException;
-
- @Query("WHERE open = true AND sortKey > ? ORDER BY sortKey LIMIT ?")
- ResultSet<Change> allOpenPrev(String sortKey, int limit) throws OrmException;
-
- @Query("WHERE open = true AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
- ResultSet<Change> allOpenNext(String sortKey, int limit) throws OrmException;
-
- @Query("WHERE open = true AND dest.projectName = ?")
- ResultSet<Change> byProjectOpenAll(Project.NameKey p) throws OrmException;
-
- @Query("WHERE open = true AND dest.projectName = ? AND sortKey > ?"
- + " ORDER BY sortKey LIMIT ?")
- ResultSet<Change> byProjectOpenPrev(Project.NameKey p, String sortKey,
- int limit) throws OrmException;
-
- @Query("WHERE open = true AND dest.projectName = ? AND sortKey < ?"
- + " ORDER BY sortKey DESC LIMIT ?")
- ResultSet<Change> byProjectOpenNext(Project.NameKey p, String sortKey,
- int limit) throws OrmException;
-
- @Query("WHERE open = false AND status = ? AND dest.projectName = ? AND sortKey > ?"
- + " ORDER BY sortKey LIMIT ?")
- ResultSet<Change> byProjectClosedPrev(char status, Project.NameKey p,
- String sortKey, int limit) throws OrmException;
-
- @Query("WHERE open = false AND status = ? AND dest.projectName = ? AND sortKey < ?"
- + " ORDER BY sortKey DESC LIMIT ?")
- ResultSet<Change> byProjectClosedNext(char status, Project.NameKey p,
- String sortKey, int limit) throws OrmException;
-
- @Query("WHERE open = false AND status = ? AND sortKey > ? ORDER BY sortKey LIMIT ?")
- ResultSet<Change> allClosedPrev(char status, String sortKey, int limit)
- throws OrmException;
-
- @Query("WHERE open = false AND status = ? AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
- ResultSet<Change> allClosedNext(char status, String sortKey, int limit)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessage.java b/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessage.java
deleted file mode 100644
index 521aa7ff48..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessage.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-import java.sql.Timestamp;
-
-/** A message attached to a {@link Change}. */
-public final class ChangeMessage {
- public static class Key extends StringKey<Change.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Change.Id changeId;
-
- @Column(length = 40)
- protected String uuid;
-
- protected Key() {
- changeId = new Change.Id();
- }
-
- public Key(final Change.Id change, final String uuid) {
- this.changeId = change;
- this.uuid = uuid;
- }
-
- @Override
- public Change.Id getParentKey() {
- return changeId;
- }
-
- @Override
- public String get() {
- return uuid;
- }
-
- @Override
- protected void set(String newValue) {
- uuid = newValue;
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- /** Who wrote this comment; null if it was written by the Gerrit system. */
- @Column(name = "author_id", notNull = false)
- protected Account.Id author;
-
- /** When this comment was drafted. */
- @Column
- protected Timestamp writtenOn;
-
- /** The text left by the user. */
- @Column(notNull = false, length = Integer.MAX_VALUE)
- protected String message;
-
- protected ChangeMessage() {
- }
-
- public ChangeMessage(final ChangeMessage.Key k, final Account.Id a) {
- this(k, a, new Timestamp(System.currentTimeMillis()));
- }
-
- public ChangeMessage(final ChangeMessage.Key k, final Account.Id a,
- final Timestamp wo) {
- key = k;
- author = a;
- writtenOn = wo;
- }
-
- public ChangeMessage.Key getKey() {
- return key;
- }
-
- /** If null, the message was written 'by the Gerrit system'. */
- public Account.Id getAuthor() {
- return author;
- }
-
- public void setAuthor(final Account.Id accountId) {
- if (author != null) {
- throw new IllegalStateException("Cannot modify author once assigned");
- }
- author = accountId;
- }
-
- public Timestamp getWrittenOn() {
- return writtenOn;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(final String s) {
- message = s;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessageAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessageAccess.java
deleted file mode 100644
index da95329f2d..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ChangeMessageAccess.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface ChangeMessageAccess extends
- Access<ChangeMessage, ChangeMessage.Key> {
- @PrimaryKey("key")
- ChangeMessage get(ChangeMessage.Key id) throws OrmException;
-
- @Query("WHERE key.changeId = ? ORDER BY writtenOn")
- ResultSet<ChangeMessage> byChange(Change.Id id) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ContactInformation.java b/src/main/java/com/google/gerrit/client/reviewdb/ContactInformation.java
deleted file mode 100644
index 660e66aa48..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ContactInformation.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-
-/** Non-Internet contact details, such as a postal address and telephone. */
-public final class ContactInformation {
- @Column(length = Integer.MAX_VALUE, notNull = false)
- protected String address;
-
- @Column(notNull = false, length = 40)
- protected String country;
-
- @Column(notNull = false, length = 30)
- protected String phoneNbr;
-
- @Column(notNull = false, length = 30)
- protected String faxNbr;
-
- public ContactInformation() {
- }
-
- public String getAddress() {
- return address;
- }
-
- public void setAddress(final String a) {
- address = a;
- }
-
- public String getCountry() {
- return country;
- }
-
- public void setCountry(final String c) {
- country = c;
- }
-
- public String getPhoneNumber() {
- return phoneNbr;
- }
-
- public void setPhoneNumber(final String p) {
- phoneNbr = p;
- }
-
- public String getFaxNumber() {
- return faxNbr;
- }
-
- public void setFaxNumber(final String f) {
- faxNbr = f;
- }
-
- public static boolean hasData(final ContactInformation contactInformation) {
- if (contactInformation == null) {
- return false;
- }
- return hasData(contactInformation.address)
- || hasData(contactInformation.country)
- || hasData(contactInformation.phoneNbr)
- || hasData(contactInformation.faxNbr);
- }
-
- public static boolean hasAddress(final ContactInformation contactInformation) {
- return contactInformation != null && hasData(contactInformation.address);
- }
-
- private static boolean hasData(final String s) {
- return s != null && s.trim().length() > 0;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreement.java b/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreement.java
deleted file mode 100644
index aff32ccf9a..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreement.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-/**
- * An agreement {@link Account} must acknowledge to contribute changes.
- *
- * @see AccountAgreement
- */
-public final class ContributorAgreement {
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column(name = "cla_id")
- protected int id;
-
- protected Id() {
- }
-
- public Id(final int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
- }
-
- @Column
- protected Id id;
-
- /** Is this an active agreement contributors can use. */
- @Column
- protected boolean active;
-
- /** Does this agreement require the {@link Account} to have contact details? */
- @Column
- protected boolean requireContactInformation;
-
- /** Does this agreement automatically verify new accounts? */
- @Column
- protected boolean autoVerify;
-
- /** A short name for the agreement. */
- @Column(length = 40)
- protected String shortName;
-
- /** A short one-line description text to appear next to the name. */
- @Column(notNull = false)
- protected String shortDescription;
-
- /** Web address of the agreement documentation. */
- @Column
- protected String agreementUrl;
-
- protected ContributorAgreement() {
- }
-
- /**
- * Create a new agreement.
- *
- * @param newId unique id, see {@link ReviewDb#nextAccountId()}.
- * @param name a short title/name for the agreement.
- */
- public ContributorAgreement(final ContributorAgreement.Id newId,
- final String name) {
- id = newId;
- shortName = name;
- }
-
- public ContributorAgreement.Id getId() {
- return id;
- }
-
- public boolean isActive() {
- return active;
- }
-
- public void setActive(final boolean a) {
- active = a;
- }
-
- public boolean isAutoVerify() {
- return autoVerify;
- }
-
- public void setAutoVerify(final boolean g) {
- autoVerify = g;
- }
-
- public boolean isRequireContactInformation() {
- return requireContactInformation;
- }
-
- public void setRequireContactInformation(final boolean r) {
- requireContactInformation = r;
- }
-
- public String getShortName() {
- return shortName;
- }
-
- public String getShortDescription() {
- return shortDescription;
- }
-
- public void setShortDescription(final String d) {
- shortDescription = d;
- }
-
- public String getAgreementUrl() {
- return agreementUrl;
- }
-
- public void setAgreementUrl(final String h) {
- agreementUrl = h;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreementAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreementAccess.java
deleted file mode 100644
index 9c40c90714..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ContributorAgreementAccess.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-/** Access interface for {@link ContributorAgreement}. */
-public interface ContributorAgreementAccess extends
- Access<ContributorAgreement, ContributorAgreement.Id> {
- @PrimaryKey("id")
- ContributorAgreement get(ContributorAgreement.Id key) throws OrmException;
-
- @Query("WHERE active = true ORDER BY shortName")
- ResultSet<ContributorAgreement> active() throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Patch.java b/src/main/java/com/google/gerrit/client/reviewdb/Patch.java
deleted file mode 100644
index 73ddd87607..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/Patch.java
+++ /dev/null
@@ -1,276 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gerrit.client.rpc.CodedEnum;
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** A single modified file in a {@link PatchSet}. */
-public final class Patch {
- public static class Key extends StringKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column(name = Column.NONE)
- protected PatchSet.Id patchSetId;
-
- @Column
- protected String fileName;
-
- protected Key() {
- patchSetId = new PatchSet.Id();
- }
-
- public Key(final PatchSet.Id ps, final String name) {
- this.patchSetId = ps;
- this.fileName = name;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- @Override
- public String get() {
- return fileName;
- }
-
- @Override
- protected void set(String newValue) {
- fileName = newValue;
- }
-
- /** Parse a Patch.Id out of a string representation. */
- public static Key parse(final String str) {
- final Key r = new Key();
- r.fromString(str);
- return r;
- }
-
- public String getFileName() {
- return get();
- }
- }
-
- /** Type of modification made to the file path. */
- public static enum ChangeType implements CodedEnum {
- /** Path is being created/introduced by this patch. */
- ADDED('A'),
-
- /** Path already exists, and has updated content. */
- MODIFIED('M'),
-
- /** Path existed, but is being removed by this patch. */
- DELETED('D'),
-
- /** Path existed at {@link Patch#getSourceFileName()} but was moved. */
- RENAMED('R'),
-
- /** Path was copied from {@link Patch#getSourceFileName()}. */
- COPIED('C');
-
- private final char code;
-
- private ChangeType(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static ChangeType forCode(final char c) {
- for (final ChangeType s : ChangeType.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- /** Type of formatting for this patch. */
- public static enum PatchType implements CodedEnum {
- /**
- * A textual difference between two versions.
- *
- * <p>
- * A UNIFIED patch can be rendered in multiple ways. Most commonly, it is
- * rendered as a side by side display using two columns, left column for the
- * old version, right column for the new version. A UNIFIED patch can also
- * be formatted in a number of standard "patch script" styles, but typically
- * is formatted in the POSIX standard unified diff format.
- *
- * <p>
- * Usually Gerrit renders a UNIFIED patch in a
- * {@link com.google.gerrit.client.patches.PatchScreen.SideBySide} view,
- * presenting the file in two columns. If the user chooses, a
- * {@link com.google.gerrit.client.patches.PatchScreen.Unified} is also a
- * valid display method.
- * */
- UNIFIED('U'),
-
- /**
- * Difference of two (or more) binary contents.
- *
- * <p>
- * A BINARY patch cannot be viewed in a text display, as it represents a
- * change in binary content at the associated path, for example, an image
- * file has been replaced with a different image.
- *
- * <p>
- * Gerrit can only render a BINARY file in a
- * {@link com.google.gerrit.client.patches.PatchScreen.Unified} view, as the
- * only information it can display is the old and new file content hashes.
- */
- BINARY('B'),
-
- /**
- * Difference of three or more textual contents.
- *
- * <p>
- * Git can produce an n-way unified diff, showing how a merge conflict was
- * resolved when two or more conflicting branches were merged together in a
- * single merge commit.
- *
- * <p>
- * This type of patch can only appear if there are two or more
- * {@link PatchSetAncestor} entities for the same parent {@link PatchSet},
- * as that denotes that the patch set is a merge commit.
- *
- * <p>
- * Gerrit can only render an N_WAY file in a
- * {@link com.google.gerrit.client.patches.PatchScreen.Unified} view, as it
- * does not have code to split the n-way unified diff into multiple edit
- * lists, one per pre-image. However, a logical way to display this format
- * would be an n-way table, with n+1 columns displayed (n pre-images, +1
- * post-image).
- */
- N_WAY('N');
-
- private final char code;
-
- private PatchType(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static PatchType forCode(final char c) {
- for (final PatchType s : PatchType.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- protected Key key;
-
- /** What sort of change is this to the path; see {@link ChangeType}. */
- protected char changeType;
-
- /** What type of patch is this; see {@link PatchType}. */
- protected char patchType;
-
- /** Number of published comments on this patch. */
- protected int nbrComments;
-
- /** Number of drafts by the current user; not persisted in the datastore. */
- protected int nbrDrafts;
-
- /**
- * Original if {@link #changeType} is {@link ChangeType#COPIED} or
- * {@link ChangeType#RENAMED}.
- */
- protected String sourceFileName;
-
- /** True if this patch has been reviewed by the current logged in user */
- private boolean reviewedByCurrentUser;
-
- protected Patch() {
- }
-
- public Patch(final Patch.Key newId) {
- key = newId;
- setChangeType(ChangeType.MODIFIED);
- setPatchType(PatchType.UNIFIED);
- }
-
- public Patch.Key getKey() {
- return key;
- }
-
- public int getCommentCount() {
- return nbrComments;
- }
-
- public void setCommentCount(final int n) {
- nbrComments = n;
- }
-
- public int getDraftCount() {
- return nbrDrafts;
- }
-
- public void setDraftCount(final int n) {
- nbrDrafts = n;
- }
-
- public ChangeType getChangeType() {
- return ChangeType.forCode(changeType);
- }
-
- public void setChangeType(final ChangeType type) {
- changeType = type.getCode();
- }
-
- public PatchType getPatchType() {
- return PatchType.forCode(patchType);
- }
-
- public void setPatchType(final PatchType type) {
- patchType = type.getCode();
- }
-
- public String getFileName() {
- return key.fileName;
- }
-
- public String getSourceFileName() {
- return sourceFileName;
- }
-
- public void setSourceFileName(final String n) {
- sourceFileName = n;
- }
-
- public boolean isReviewedByCurrentUser() {
- return reviewedByCurrentUser;
- }
-
- public void setReviewedByCurrentUser(boolean r) {
- reviewedByCurrentUser = r;
- }
-
- @Override
- public String toString() {
- return "[Patch " + getKey().toString() + "]";
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchLineComment.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchLineComment.java
deleted file mode 100644
index 90dcc2f6e3..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchLineComment.java
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-import java.sql.Timestamp;
-
-/** A comment left by a user on a specific line of a {@link Patch}. */
-public final class PatchLineComment {
- public static class Key extends StringKey<Patch.Key> {
- private static final long serialVersionUID = 1L;
-
- @Column(name = Column.NONE)
- protected Patch.Key patchKey;
-
- @Column(length = 40)
- protected String uuid;
-
- protected Key() {
- patchKey = new Patch.Key();
- }
-
- public Key(final Patch.Key p, final String uuid) {
- this.patchKey = p;
- this.uuid = uuid;
- }
-
- @Override
- public Patch.Key getParentKey() {
- return patchKey;
- }
-
- @Override
- public String get() {
- return uuid;
- }
-
- @Override
- protected void set(String newValue) {
- uuid = newValue;
- }
- }
-
- protected static final char STATUS_DRAFT = 'd';
- protected static final char STATUS_PUBLISHED = 'P';
-
- public static enum Status {
- DRAFT(STATUS_DRAFT),
-
- PUBLISHED(STATUS_PUBLISHED);
-
- private final char code;
-
- private Status(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static Status forCode(final char c) {
- for (final Status s : Status.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- /** Line number this comment applies to; it should display after the line. */
- @Column
- protected int lineNbr;
-
- /** Who wrote this comment. */
- @Column(name = "author_id")
- protected Account.Id author;
-
- /** When this comment was drafted. */
- @Column
- protected Timestamp writtenOn;
-
- /** Current publication state of the comment; see {@link Status}. */
- @Column
- protected char status;
-
- /** Which file is this comment; 0 is ancestor, 1 is new version. */
- @Column
- protected short side;
-
- /** The text left by the user. */
- @Column(notNull = false, length = Integer.MAX_VALUE)
- protected String message;
-
- /** The parent of this comment, or null if this is the first comment on this line */
- @Column(length = 40, notNull = false)
- protected String parentUuid;
-
- protected PatchLineComment() {
- }
-
- public PatchLineComment(final PatchLineComment.Key id, final int line,
- final Account.Id a, String parentUuid) {
- key = id;
- lineNbr = line;
- author = a;
- this.parentUuid = parentUuid;
- setStatus(Status.DRAFT);
- updated();
- }
-
- public PatchLineComment.Key getKey() {
- return key;
- }
-
- public int getLine() {
- return lineNbr;
- }
-
- public Account.Id getAuthor() {
- return author;
- }
-
- public Timestamp getWrittenOn() {
- return writtenOn;
- }
-
- public Status getStatus() {
- return Status.forCode(status);
- }
-
- public void setStatus(final Status s) {
- status = s.getCode();
- }
-
- public short getSide() {
- return side;
- }
-
- public void setSide(final short s) {
- side = s;
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(final String s) {
- message = s;
- }
-
- public void updated() {
- writtenOn = new Timestamp(System.currentTimeMillis());
- }
-
- public String getParentUuid() {
- return parentUuid;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchLineCommentAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchLineCommentAccess.java
deleted file mode 100644
index 59bff87e69..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchLineCommentAccess.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface PatchLineCommentAccess extends
- Access<PatchLineComment, PatchLineComment.Key> {
- @PrimaryKey("key")
- PatchLineComment get(PatchLineComment.Key id) throws OrmException;
-
- @Query("WHERE key.patchKey = ? AND status = '"
- + PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> published(Patch.Key patch) throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId.changeId = ?"
- + " AND key.patchKey.fileName = ? AND status = '"
- + PatchLineComment.STATUS_PUBLISHED + "' ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> published(Change.Id id, String file)
- throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId = ? AND status = '"
- + PatchLineComment.STATUS_PUBLISHED + "'")
- ResultSet<PatchLineComment> published(PatchSet.Id patchset)
- throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId = ? AND status = '"
- + PatchLineComment.STATUS_DRAFT
- + "' AND author = ? ORDER BY key.patchKey,lineNbr,writtenOn")
- ResultSet<PatchLineComment> draft(PatchSet.Id patchset, Account.Id author)
- throws OrmException;
-
- @Query("WHERE key.patchKey = ? AND status = '"
- + PatchLineComment.STATUS_DRAFT
- + "' AND author = ? ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> draft(Patch.Key patch, Account.Id author)
- throws OrmException;
-
- @Query("WHERE key.patchKey.patchSetId.changeId = ?"
- + " AND key.patchKey.fileName = ? AND author = ? AND status = '"
- + PatchLineComment.STATUS_DRAFT + "' ORDER BY lineNbr,writtenOn")
- ResultSet<PatchLineComment> draft(Change.Id id, String file, Account.Id author)
- throws OrmException;
-
- @Query("WHERE status = '" + PatchLineComment.STATUS_DRAFT
- + "' AND author = ?")
- ResultSet<PatchLineComment> draftByAuthor(Account.Id author)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSet.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSet.java
deleted file mode 100644
index f60ad7f44e..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSet.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-import java.sql.Timestamp;
-
-/** A single revision of a {@link Change}. */
-public final class PatchSet {
- private static final String REFS_CHANGES = "refs/changes/";
-
- /** Is the reference name a change reference? */
- public static boolean isRef(final String name) {
- return name.matches("^refs/changes/.*/[1-9][0-9]*/[1-9][0-9]*$");
- }
-
- public static class Id extends IntKey<Change.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Change.Id changeId;
-
- @Column
- protected int patchSetId;
-
- protected Id() {
- changeId = new Change.Id();
- }
-
- public Id(final Change.Id change, final int id) {
- this.changeId = change;
- this.patchSetId = id;
- }
-
- @Override
- public Change.Id getParentKey() {
- return changeId;
- }
-
- @Override
- public int get() {
- return patchSetId;
- }
-
- @Override
- protected void set(int newValue) {
- patchSetId = newValue;
- }
-
- /** Parse a PatchSet.Id out of a string representation. */
- public static Id parse(final String str) {
- final Id r = new Id();
- r.fromString(str);
- return r;
- }
-
- /** Parse a PatchSet.Id from a {@link PatchSet#getRefName()} result. */
- public static Id fromRef(String name) {
- if (!name.startsWith(REFS_CHANGES)) {
- throw new IllegalArgumentException("Not a PatchSet.Id: " + name);
- }
- final String[] parts = name.substring(REFS_CHANGES.length()).split("/");
- final int n = parts.length;
- if (n < 2) {
- throw new IllegalArgumentException("Not a PatchSet.Id: " + name);
- }
- final int changeId = Integer.parseInt(parts[n - 2]);
- final int patchSetId = Integer.parseInt(parts[n - 1]);
- return new PatchSet.Id(new Change.Id(changeId), patchSetId);
- }
- }
-
- @Column(name = Column.NONE)
- protected Id id;
-
- @Column(notNull = false)
- protected RevId revision;
-
- @Column(name = "uploader_account_id")
- protected Account.Id uploader;
-
- /** When this patch set was first introduced onto the change. */
- @Column
- protected Timestamp createdOn;
-
- protected PatchSet() {
- }
-
- public PatchSet(final PatchSet.Id k) {
- id = k;
- }
-
- public PatchSet.Id getId() {
- return id;
- }
-
- public int getPatchSetId() {
- return id.get();
- }
-
- public RevId getRevision() {
- return revision;
- }
-
- public void setRevision(final RevId i) {
- revision = i;
- }
-
- public Account.Id getUploader() {
- return uploader;
- }
-
- public void setUploader(final Account.Id who) {
- uploader = who;
- }
-
- public Timestamp getCreatedOn() {
- return createdOn;
- }
-
- public void setCreatedOn(final Timestamp ts) {
- createdOn = ts;
- }
-
- public String getRefName() {
- final StringBuilder r = new StringBuilder();
- r.append(REFS_CHANGES);
- final int changeId = id.getParentKey().get();
- final int m = changeId % 100;
- if (m < 10) {
- r.append('0');
- }
- r.append(m);
- r.append('/');
- r.append(changeId);
- r.append('/');
- r.append(id.get());
- return r.toString();
- }
-
- @Override
- public String toString() {
- return "[PatchSet " + getId().toString() + "]";
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAccess.java
deleted file mode 100644
index 72df200b39..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAccess.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface PatchSetAccess extends Access<PatchSet, PatchSet.Id> {
- @PrimaryKey("id")
- PatchSet get(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE id.changeId = ? ORDER BY id.patchSetId")
- ResultSet<PatchSet> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE id.changeId = ? AND revision = ?")
- ResultSet<PatchSet> byChangeRevision(Change.Id id, RevId rev)
- throws OrmException;
-
- @Query("WHERE revision = ? LIMIT 2")
- ResultSet<PatchSet> byRevision(RevId rev) throws OrmException;
-
- @Query("WHERE revision >= ? AND revision <= ? LIMIT 2")
- ResultSet<PatchSet> byRevisionRange(RevId reva, RevId revb)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestor.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestor.java
deleted file mode 100644
index 8b45848daf..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestor.java
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-
-/** Ancestors of a {@link PatchSet} that the PatchSet depends upon. */
-public final class PatchSetAncestor {
- public static class Id extends IntKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column(name = Column.NONE)
- protected PatchSet.Id patchSetId;
-
- @Column
- protected int position;
-
- protected Id() {
- patchSetId = new PatchSet.Id();
- }
-
- public Id(final PatchSet.Id psId, final int pos) {
- this.patchSetId = psId;
- this.position = pos;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- @Override
- public int get() {
- return position;
- }
-
- @Override
- protected void set(int newValue) {
- position = newValue;
- }
- }
-
- @Column(name = Column.NONE)
- protected Id key;
-
- @Column
- protected RevId ancestorRevision;
-
- protected PatchSetAncestor() {
- }
-
- public PatchSetAncestor(final PatchSetAncestor.Id k) {
- key = k;
- }
-
- public PatchSetAncestor.Id getId() {
- return key;
- }
-
- public PatchSet.Id getPatchSet() {
- return key.patchSetId;
- }
-
- public int getPosition() {
- return key.position;
- }
-
- public RevId getAncestorRevision() {
- return ancestorRevision;
- }
-
- public void setAncestorRevision(final RevId id) {
- ancestorRevision = id;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestorAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestorAccess.java
deleted file mode 100644
index d492fa5cf8..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetAncestorAccess.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface PatchSetAncestorAccess extends
- Access<PatchSetAncestor, PatchSetAncestor.Id> {
- @PrimaryKey("key")
- PatchSetAncestor get(PatchSetAncestor.Id key) throws OrmException;
-
- @Query("WHERE key.patchSetId = ? ORDER BY key.position")
- ResultSet<PatchSetAncestor> ancestorsOf(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE ancestorRevision = ?")
- ResultSet<PatchSetAncestor> descendantsOf(RevId revision)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApproval.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApproval.java
deleted file mode 100644
index 75a4660446..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApproval.java
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-import java.sql.Timestamp;
-
-/** An approval (or negative approval) on a patch set. */
-public final class PatchSetApproval {
- public static class Key extends CompoundKey<PatchSet.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column(name = Column.NONE)
- protected PatchSet.Id patchSetId;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected ApprovalCategory.Id categoryId;
-
- protected Key() {
- patchSetId = new PatchSet.Id();
- accountId = new Account.Id();
- categoryId = new ApprovalCategory.Id();
- }
-
- public Key(final PatchSet.Id ps, final Account.Id a,
- final ApprovalCategory.Id c) {
- this.patchSetId = ps;
- this.accountId = a;
- this.categoryId = c;
- }
-
- @Override
- public PatchSet.Id getParentKey() {
- return patchSetId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {accountId, categoryId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- /**
- * Value assigned by the user.
- * <p>
- * The precise meaning of "value" is up to each category.
- * <p>
- * In general:
- * <ul>
- * <li><b>&lt; 0:</b> The approval is rejected/revoked.</li>
- * <li><b>= 0:</b> No indication either way is provided.</li>
- * <li><b>&gt; 0:</b> The approval is approved/positive.</li>
- * </ul>
- * and in the negative and positive direction a magnitude can be assumed.The
- * further from 0 the more assertive the approval.
- */
- @Column
- protected short value;
-
- @Column
- protected Timestamp granted;
-
- /** <i>Cached copy of Change.open.</i> */
- @Column
- protected boolean changeOpen;
-
- /** <i>Cached copy of Change.sortKey</i>; only if {@link #changeOpen} = false */
- @Column(length = 16, notNull = false)
- protected String changeSortKey;
-
- protected PatchSetApproval() {
- }
-
- public PatchSetApproval(final PatchSetApproval.Key k, final short v) {
- key = k;
- changeOpen = true;
- setValue(v);
- setGranted();
- }
-
- public PatchSetApproval(final PatchSet.Id psId, final PatchSetApproval src) {
- key =
- new PatchSetApproval.Key(psId, src.getAccountId(), src.getCategoryId());
- changeOpen = true;
- value = src.getValue();
- granted = src.granted;
- }
-
- public PatchSetApproval.Key getKey() {
- return key;
- }
-
- public PatchSet.Id getPatchSetId() {
- return key.patchSetId;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public ApprovalCategory.Id getCategoryId() {
- return key.categoryId;
- }
-
- public short getValue() {
- return value;
- }
-
- public void setValue(final short v) {
- value = v;
- }
-
- public Timestamp getGranted() {
- return granted;
- }
-
- public void setGranted() {
- granted = new Timestamp(System.currentTimeMillis());
- }
-
- public void cache(final Change c) {
- changeOpen = c.open;
- changeSortKey = c.sortKey;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApprovalAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApprovalAccess.java
deleted file mode 100644
index c74f4e4e50..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetApprovalAccess.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface PatchSetApprovalAccess extends
- Access<PatchSetApproval, PatchSetApproval.Key> {
- @PrimaryKey("key")
- PatchSetApproval get(PatchSetApproval.Key key) throws OrmException;
-
- @Query("WHERE key.patchSetId.changeId = ?")
- ResultSet<PatchSetApproval> byChange(Change.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId = ?")
- ResultSet<PatchSetApproval> byPatchSet(PatchSet.Id id) throws OrmException;
-
- @Query("WHERE key.patchSetId = ? AND key.accountId = ?")
- ResultSet<PatchSetApproval> byPatchSetUser(PatchSet.Id patchSet,
- Account.Id account) throws OrmException;
-
- @Query("WHERE changeOpen = true AND key.accountId = ?")
- ResultSet<PatchSetApproval> openByUser(Account.Id account)
- throws OrmException;
-
- @Query("WHERE changeOpen = false AND key.accountId = ?"
- + " ORDER BY changeSortKey DESC LIMIT 10")
- ResultSet<PatchSetApproval> closedByUser(Account.Id account)
- throws OrmException;
-
- @Query("WHERE changeOpen = false AND key.accountId = ? ORDER BY changeSortKey")
- ResultSet<PatchSetApproval> closedByUserAll(Account.Id account)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetInfo.java b/src/main/java/com/google/gerrit/client/reviewdb/PatchSetInfo.java
deleted file mode 100644
index d486d584b3..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/PatchSetInfo.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-
-/**
- * Additional data about a {@link PatchSet} not normally loaded.
- *
- * @see com.google.gerrit.server.patch.PatchSetInfoFactory
- */
-public final class PatchSetInfo {
- protected PatchSet.Id key;
-
- /** First line of {@link #message}. */
- protected String subject;
-
- /** The complete description of the change the patch set introduces. */
- protected String message;
-
- /** Identity of who wrote the patch set. May differ from {@link #committer}. */
- protected UserIdentity author;
-
- /** Identity of who committed the patch set to the VCS. */
- protected UserIdentity committer;
-
- protected PatchSetInfo() {
- }
-
- public PatchSetInfo(final PatchSet.Id k) {
- key = k;
- }
-
- public PatchSet.Id getKey() {
- return key;
- }
-
- public String getSubject() {
- return subject;
- }
-
- public void setSubject(final String s) {
- if (s != null && s.length() > 255) {
- subject = s.substring(0, 255);
- } else {
- subject = s;
- }
- }
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(final String m) {
- message = m;
- }
-
- public UserIdentity getAuthor() {
- return author;
- }
-
- public void setAuthor(final UserIdentity u) {
- author = u;
- }
-
- public UserIdentity getCommitter() {
- return committer;
- }
-
- public void setCommitter(final UserIdentity u) {
- committer = u;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/Project.java b/src/main/java/com/google/gerrit/client/reviewdb/Project.java
deleted file mode 100644
index f6d08a17ad..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/Project.java
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.IntKey;
-import com.google.gwtorm.client.StringKey;
-
-/** Projects match a source code repository managed by Gerrit */
-public final class Project {
- /** Project name key */
- public static class NameKey extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected String name;
-
- protected NameKey() {
- }
-
- public NameKey(final String n) {
- name = n;
- }
-
- @Override
- public String get() {
- return name;
- }
-
- @Override
- protected void set(String newValue) {
- name = newValue;
- }
-
- /** Parse a Project.NameKey out of a string representation. */
- public static NameKey parse(final String str) {
- final NameKey r = new NameKey();
- r.fromString(str);
- return r;
- }
- }
-
- /** Synthetic key to link to within the database */
- public static class Id extends IntKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected int id;
-
- protected Id() {
- }
-
- public Id(final int id) {
- this.id = id;
- }
-
- @Override
- public int get() {
- return id;
- }
-
- @Override
- protected void set(int newValue) {
- id = newValue;
- }
- }
-
- public static enum SubmitType {
- FAST_FORWARD_ONLY('F'),
-
- MERGE_IF_NECESSARY('M'),
-
- MERGE_ALWAYS('A'),
-
- CHERRY_PICK('C');
-
- private final char code;
-
- private SubmitType(final char c) {
- code = c;
- }
-
- public char getCode() {
- return code;
- }
-
- public static SubmitType forCode(final char c) {
- for (final SubmitType s : SubmitType.values()) {
- if (s.code == c) {
- return s;
- }
- }
- return null;
- }
- }
-
- @Column
- protected NameKey name;
-
- @Column
- protected Id projectId;
-
- @Column(length = Integer.MAX_VALUE, notNull = false)
- protected String description;
-
- @Column
- protected boolean useContributorAgreements;
-
- @Column
- protected boolean useSignedOffBy;
-
- @Column
- protected char submitType;
-
- protected Project() {
- }
-
- public Project(final Project.NameKey newName, final Project.Id newId) {
- name = newName;
- projectId = newId;
- useContributorAgreements = true;
- setSubmitType(SubmitType.MERGE_IF_NECESSARY);
- }
-
- public Project.Id getId() {
- return projectId;
- }
-
- public Project.NameKey getNameKey() {
- return name;
- }
-
- public String getName() {
- return name.get();
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(final String d) {
- description = d;
- }
-
- public boolean isUseContributorAgreements() {
- return useContributorAgreements;
- }
-
- public void setUseContributorAgreements(final boolean u) {
- useContributorAgreements = u;
- }
-
- public boolean isUseSignedOffBy() {
- return useSignedOffBy;
- }
-
- public void setUseSignedOffBy(final boolean sbo) {
- useSignedOffBy = sbo;
- }
-
- public SubmitType getSubmitType() {
- return SubmitType.forCode(submitType);
- }
-
- public void setSubmitType(final SubmitType type) {
- submitType = type.getCode();
- }
-
- public void copySettingsFrom(final Project update) {
- description = update.description;
- useContributorAgreements = update.useContributorAgreements;
- useSignedOffBy = update.useSignedOffBy;
- submitType = update.submitType;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java
deleted file mode 100644
index 7b6030e803..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ProjectAccess.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.SecondaryKey;
-
-public interface ProjectAccess extends Access<Project, Project.NameKey> {
- @PrimaryKey("name")
- Project get(Project.NameKey name) throws OrmException;
-
- @SecondaryKey("projectId")
- Project get(Project.Id id) throws OrmException;
-
- @Query("ORDER BY name")
- ResultSet<Project> all() throws OrmException;
-
- @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?")
- ResultSet<Project> suggestByName(String nameA, String nameB, int limit)
- throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ProjectRight.java b/src/main/java/com/google/gerrit/client/reviewdb/ProjectRight.java
deleted file mode 100644
index 46b6d60710..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ProjectRight.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-/** Grant to use an {@link ApprovalCategory} in the scope of a {@link Project}. */
-public final class ProjectRight {
- public static class Key extends CompoundKey<Project.NameKey> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Project.NameKey projectName;
-
- @Column
- protected ApprovalCategory.Id categoryId;
-
- @Column
- protected AccountGroup.Id groupId;
-
- protected Key() {
- projectName = new Project.NameKey();
- categoryId = new ApprovalCategory.Id();
- groupId = new AccountGroup.Id();
- }
-
- public Key(final Project.NameKey p, final ApprovalCategory.Id a,
- final AccountGroup.Id g) {
- projectName = p;
- categoryId = a;
- groupId = g;
- }
-
- @Override
- public Project.NameKey getParentKey() {
- return projectName;
- }
-
- public Project.NameKey getProjectNameKey() {
- return projectName;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {categoryId, groupId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- @Column
- protected short minValue;
-
- @Column
- protected short maxValue;
-
- protected ProjectRight() {
- }
-
- public ProjectRight(final ProjectRight.Key k) {
- key = k;
- }
-
- public ProjectRight.Key getKey() {
- return key;
- }
-
- public Project.NameKey getProjectNameKey() {
- return key.projectName;
- }
-
- public ApprovalCategory.Id getApprovalCategoryId() {
- return key.categoryId;
- }
-
- public AccountGroup.Id getAccountGroupId() {
- return key.groupId;
- }
-
- public short getMinValue() {
- return minValue;
- }
-
- public void setMinValue(final short m) {
- minValue = m;
- }
-
- public short getMaxValue() {
- return maxValue;
- }
-
- public void setMaxValue(final short m) {
- maxValue = m;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ProjectRightAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/ProjectRightAccess.java
deleted file mode 100644
index 4f722f9608..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ProjectRightAccess.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface ProjectRightAccess extends
- Access<ProjectRight, ProjectRight.Key> {
- @PrimaryKey("key")
- ProjectRight get(ProjectRight.Key key) throws OrmException;
-
- @Query("WHERE key.projectName = ?")
- ResultSet<ProjectRight> byProject(Project.NameKey name) throws OrmException;
-
- @Query("WHERE key.categoryId = ? AND key.groupId = ?")
- ResultSet<ProjectRight> byCategoryGroup(ApprovalCategory.Id cat,
- AccountGroup.Id group) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/RevId.java b/src/main/java/com/google/gerrit/client/reviewdb/RevId.java
deleted file mode 100644
index 642fe7a876..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/RevId.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-
-/** A revision identifier for a file or a change. */
-public final class RevId {
- public static final int LEN = 40;
-
- @Column(length = LEN)
- protected String id;
-
- protected RevId() {
- }
-
- public RevId(final String str) {
- id = str;
- }
-
- /** @return the value of this revision id. */
- public String get() {
- return id;
- }
-
- /** @return true if this revision id has all required digits. */
- public boolean isComplete() {
- return get().length() == LEN;
- }
-
- /**
- * @return if {@link #isComplete()}, <code>this</code>; otherwise a new RevId
- * with 'z' appended on the end.
- */
- public RevId max() {
- if (isComplete()) {
- return this;
- }
-
- final StringBuilder revEnd = new StringBuilder(get().length() + 1);
- revEnd.append(get());
- revEnd.append('z');
- return new RevId(revEnd.toString());
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java b/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
deleted file mode 100644
index 3a2fc6d5f2..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/ReviewDb.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Relation;
-import com.google.gwtorm.client.Schema;
-import com.google.gwtorm.client.Sequence;
-
-/**
- * The review service database schema.
- * <p>
- * Root entities that are at the top level of some important data graph:
- * <ul>
- * <li>{@link Project}: Configuration for a single Git repository.</li>
- * <li>{@link Account}: Per-user account registration, preferences, identity.</li>
- * <li>{@link Change}: All review information about a single proposed change.</li>
- * <li>{@link SystemConfig}: Server-wide settings, managed by administrator.</li>
- * </ul>
- */
-public interface ReviewDb extends Schema {
- public static final int VERSION = 19;
-
- @Relation
- SchemaVersionAccess schemaVersion();
-
- @Relation
- SystemConfigAccess systemConfig();
-
- @Relation
- ApprovalCategoryAccess approvalCategories();
-
- @Relation
- ApprovalCategoryValueAccess approvalCategoryValues();
-
- @Relation
- ContributorAgreementAccess contributorAgreements();
-
- @Relation
- AccountAccess accounts();
-
- @Relation
- AccountExternalIdAccess accountExternalIds();
-
- @Relation
- AccountSshKeyAccess accountSshKeys();
-
- @Relation
- AccountAgreementAccess accountAgreements();
-
- @Relation
- AccountGroupAccess accountGroups();
-
- @Relation
- AccountGroupMemberAccess accountGroupMembers();
-
- @Relation
- AccountGroupMemberAuditAccess accountGroupMembersAudit();
-
- @Relation
- AccountGroupAgreementAccess accountGroupAgreements();
-
- @Relation
- StarredChangeAccess starredChanges();
-
- @Relation
- AccountProjectWatchAccess accountProjectWatches();
-
- @Relation
- AccountPatchReviewAccess accountPatchReviews();
-
- @Relation
- ProjectAccess projects();
-
- @Relation
- ProjectRightAccess projectRights();
-
- @Relation
- ChangeAccess changes();
-
- @Relation
- PatchSetApprovalAccess patchSetApprovals();
-
- @Relation
- ChangeMessageAccess changeMessages();
-
- @Relation
- PatchSetAccess patchSets();
-
- @Relation
- PatchSetAncestorAccess patchSetAncestors();
-
- @Relation
- PatchLineCommentAccess patchComments();
-
- /** Create the next unique id for an {@link Account}. */
- @Sequence(startWith = 1000000)
- int nextAccountId() throws OrmException;
-
- /** Create the next unique id for a {@link ContributorAgreement}. */
- @Sequence
- int nextContributorAgreementId() throws OrmException;
-
- /** Next unique id for a {@link AccountGroup}. */
- @Sequence
- int nextAccountGroupId() throws OrmException;
-
- /** Next unique id for a {@link Project}. */
- @Sequence
- int nextProjectId() throws OrmException;
-
- /** Next unique id for a {@link Change}. */
- @Sequence
- int nextChangeId() throws OrmException;
-
- /**
- * Next id for a block of {@link ChangeMessage} records.
- *
- * @see com.google.gerrit.server.ChangeUtil#messageUUID(ReviewDb)
- */
- @Sequence
- int nextChangeMessageId() throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersion.java b/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersion.java
deleted file mode 100644
index 45b27a637c..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersion.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** Current version of the database schema, to facilitate live upgrades. */
-public final class SchemaVersion {
- public static final class Key extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- private static final String VALUE = "X";
-
- @Column(length = 1)
- protected String one = VALUE;
-
- public Key() {
- }
-
- @Override
- public String get() {
- return VALUE;
- }
-
- @Override
- protected void set(final String newValue) {
- assert get().equals(newValue);
- }
- }
-
- /** Construct a new, unconfigured instance. */
- public static SchemaVersion create() {
- final SchemaVersion r = new SchemaVersion();
- r.singleton = new SchemaVersion.Key();
- return r;
- }
-
- @Column
- protected Key singleton;
-
- /** Current version number of the schema. */
- @Column
- public transient int versionNbr;
-
- protected SchemaVersion() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersionAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersionAccess.java
deleted file mode 100644
index ba37b7aa1c..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/SchemaVersionAccess.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-
-/** Access interface for {@link SchemaVersion}. */
-public interface SchemaVersionAccess extends
- Access<SchemaVersion, SchemaVersion.Key> {
- @PrimaryKey("singleton")
- SchemaVersion get(SchemaVersion.Key key) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/StarredChange.java b/src/main/java/com/google/gerrit/client/reviewdb/StarredChange.java
deleted file mode 100644
index 02c3604fbc..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/StarredChange.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.CompoundKey;
-
-/** A {@link Change} starred by an {@link Account}. */
-public class StarredChange {
- public static class Key extends CompoundKey<Account.Id> {
- private static final long serialVersionUID = 1L;
-
- @Column
- protected Account.Id accountId;
-
- @Column
- protected Change.Id changeId;
-
- protected Key() {
- accountId = new Account.Id();
- changeId = new Change.Id();
- }
-
- public Key(final Account.Id a, final Change.Id g) {
- accountId = a;
- changeId = g;
- }
-
- @Override
- public Account.Id getParentKey() {
- return accountId;
- }
-
- @Override
- public com.google.gwtorm.client.Key<?>[] members() {
- return new com.google.gwtorm.client.Key<?>[] {changeId};
- }
- }
-
- @Column(name = Column.NONE)
- protected Key key;
-
- protected StarredChange() {
- }
-
- public StarredChange(final StarredChange.Key k) {
- key = k;
- }
-
- public Account.Id getAccountId() {
- return key.accountId;
- }
-
- public Change.Id getChangeId() {
- return key.changeId;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/StarredChangeAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/StarredChangeAccess.java
deleted file mode 100644
index 8c249593a0..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/StarredChangeAccess.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-public interface StarredChangeAccess extends
- Access<StarredChange, StarredChange.Key> {
- @PrimaryKey("key")
- StarredChange get(StarredChange.Key key) throws OrmException;
-
- @Query("WHERE key.accountId = ?")
- ResultSet<StarredChange> byAccount(Account.Id id) throws OrmException;
-
- @Query("WHERE key.changeId = ?")
- ResultSet<StarredChange> byChange(Change.Id id) throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/SystemConfig.java b/src/main/java/com/google/gerrit/client/reviewdb/SystemConfig.java
deleted file mode 100644
index 99a1c85db9..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/SystemConfig.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Column;
-import com.google.gwtorm.client.StringKey;
-
-/** Global configuration needed to serve web requests. */
-public final class SystemConfig {
- public static final class Key extends
- StringKey<com.google.gwtorm.client.Key<?>> {
- private static final long serialVersionUID = 1L;
-
- private static final String VALUE = "X";
-
- @Column(length = 1)
- protected String one = VALUE;
-
- public Key() {
- }
-
- @Override
- public String get() {
- return VALUE;
- }
-
- @Override
- protected void set(final String newValue) {
- assert get().equals(newValue);
- }
- }
-
- /** Construct a new, unconfigured instance. */
- public static SystemConfig create() {
- final SystemConfig r = new SystemConfig();
- r.singleton = new SystemConfig.Key();
- return r;
- }
-
- @Column
- protected Key singleton;
-
- /** Private key to sign account identification cookies. */
- @Column(length = 36)
- public transient String registerEmailPrivateKey;
-
- /**
- * Local filesystem location of header/footer/CSS configuration files
- */
- @Column(notNull = false)
- public transient String sitePath;
-
- /** Identity of the administration group; those with full access. */
- @Column
- public AccountGroup.Id adminGroupId;
-
- /** Identity of the anonymous group, which permits anyone. */
- @Column
- public AccountGroup.Id anonymousGroupId;
-
- /** Identity of the registered users group, which permits anyone. */
- @Column
- public AccountGroup.Id registeredGroupId;
-
- protected SystemConfig() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/SystemConfigAccess.java b/src/main/java/com/google/gerrit/client/reviewdb/SystemConfigAccess.java
deleted file mode 100644
index f93d99057d..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/SystemConfigAccess.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import com.google.gwtorm.client.Access;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.PrimaryKey;
-import com.google.gwtorm.client.Query;
-import com.google.gwtorm.client.ResultSet;
-
-/** Access interface for {@link SystemConfig}. */
-public interface SystemConfigAccess extends
- Access<SystemConfig, SystemConfig.Key> {
- @PrimaryKey("singleton")
- SystemConfig get(SystemConfig.Key key) throws OrmException;
-
- @Query
- ResultSet<SystemConfig> all() throws OrmException;
-}
diff --git a/src/main/java/com/google/gerrit/client/reviewdb/UserIdentity.java b/src/main/java/com/google/gerrit/client/reviewdb/UserIdentity.java
deleted file mode 100644
index 4198d76dcc..0000000000
--- a/src/main/java/com/google/gerrit/client/reviewdb/UserIdentity.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.reviewdb;
-
-import java.sql.Timestamp;
-
-public final class UserIdentity {
- /** Full name of the user. */
- protected String name;
-
- /** Email address (or user@host style string anyway). */
- protected String email;
-
- /** Time (in UTC) when the identity was constructed. */
- protected Timestamp when;
-
- /** Offset from UTC */
- protected int tz;
-
- /** If the user has a Gerrit account, their account identity. */
- protected Account.Id accountId;
-
- public String getName() {
- return name;
- }
-
- public void setName(final String n) {
- name = n;
- }
-
- public String getEmail() {
- return email;
- }
-
- public void setEmail(final String e) {
- email = e;
- }
-
- public Timestamp getDate() {
- return when;
- }
-
- public void setDate(final Timestamp d) {
- when = d;
- }
-
- public int getTimeZone() {
- return tz;
- }
-
- public void setTimeZone(final int offset) {
- tz = offset;
- }
-
- public Account.Id getAccount() {
- return accountId;
- }
-
- public void setAccount(final Account.Id id) {
- accountId = id;
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/CodedEnum.java b/src/main/java/com/google/gerrit/client/rpc/CodedEnum.java
deleted file mode 100644
index 006860a169..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/CodedEnum.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Extension of Enum which provides distinct character code values. */
-public interface CodedEnum {
- char getCode();
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/ContactInformationStoreException.java b/src/main/java/com/google/gerrit/client/rpc/ContactInformationStoreException.java
deleted file mode 100644
index 1b1b7c1809..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/ContactInformationStoreException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the server cannot store contact information. */
-public class ContactInformationStoreException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Cannot store contact information";
-
- public ContactInformationStoreException() {
- super(MESSAGE);
- }
-
- public ContactInformationStoreException(final Throwable why) {
- super(MESSAGE, why);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/CorruptEntityException.java b/src/main/java/com/google/gerrit/client/rpc/CorruptEntityException.java
deleted file mode 100644
index ea66687256..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/CorruptEntityException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-import com.google.gwtorm.client.Key;
-
-/** Error indicating the entity's database records are invalid. */
-public class CorruptEntityException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE_PREFIX = "Corrupt Database Entity: ";
-
- public CorruptEntityException(final Key<?> key) {
- super(MESSAGE_PREFIX + key);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java b/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
deleted file mode 100644
index dd69d517b0..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/GerritCallback.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-import com.google.gerrit.client.ErrorDialog;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotSignedInDialog;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwt.user.client.rpc.InvocationException;
-import com.google.gwtjsonrpc.client.JsonUtil;
-import com.google.gwtjsonrpc.client.RemoteJsonException;
-import com.google.gwtjsonrpc.client.ServerUnavailableException;
-
-/** Abstract callback handling generic error conditions automatically */
-public abstract class GerritCallback<T> implements AsyncCallback<T> {
- public void onFailure(final Throwable caught) {
- if (isNotSignedIn(caught) || isInvalidXSRF(caught)) {
- new NotSignedInDialog().center();
-
- } else if (isNoSuchEntity(caught)) {
- new ErrorDialog(Gerrit.C.notFoundBody()).center();
-
- } else if (isNoSuchAccount(caught)) {
- final String msg = caught.getMessage();
- final String who = msg.substring(NoSuchAccountException.MESSAGE.length());
- final ErrorDialog d = new ErrorDialog(Gerrit.M.noSuchAccountMessage(who));
- d.setText(Gerrit.C.noSuchAccountTitle());
- d.center();
-
- } else if (isNameAlreadyUsed(caught)) {
- new ErrorDialog(Gerrit.C.nameAlreadyUsedBody()).center();
-
- } else if (caught instanceof ServerUnavailableException) {
- new ErrorDialog(RpcConstants.C.errorServerUnavailable()).center();
-
- } else {
- GWT.log(getClass().getName() + " caught " + caught, caught);
- new ErrorDialog(caught).center();
- }
- }
-
- private static boolean isInvalidXSRF(final Throwable caught) {
- return caught instanceof InvocationException
- && caught.getMessage().equals(JsonUtil.ERROR_INVALID_XSRF);
- }
-
- private static boolean isNotSignedIn(final Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().equals(NotSignedInException.MESSAGE);
- }
-
- protected static boolean isNoSuchEntity(final Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().equals(NoSuchEntityException.MESSAGE);
- }
-
- private static boolean isNoSuchAccount(final Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().startsWith(NoSuchAccountException.MESSAGE);
- }
-
- private static boolean isNameAlreadyUsed(final Throwable caught) {
- return caught instanceof RemoteJsonException
- && caught.getMessage().equals(NameAlreadyUsedException.MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java
deleted file mode 100644
index 9060ff9833..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/InvalidNameException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the entity name is invalid as supplied. */
-public class InvalidNameException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid Name";
-
- public InvalidNameException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java
deleted file mode 100644
index c789a343e6..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/InvalidRevisionException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the revision is invalid as supplied. */
-public class InvalidRevisionException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid Revision";
-
- public InvalidRevisionException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidSshKeyException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidSshKeyException.java
deleted file mode 100644
index 6bbcc7c04f..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/InvalidSshKeyException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the SSH key string is invalid as supplied. */
-public class InvalidSshKeyException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid SSH Key";
-
- public InvalidSshKeyException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/InvalidSshUserNameException.java b/src/main/java/com/google/gerrit/client/rpc/InvalidSshUserNameException.java
deleted file mode 100644
index 3ed8665b45..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/InvalidSshUserNameException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Error indicating the SSH user name does not match {@link Account#SSH_USER_NAME_PATTERN} pattern. */
-public class InvalidSshUserNameException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Invalid SSH user name.";
-
- public InvalidSshUserNameException() {
- super(MESSAGE);
- }
-
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/NameAlreadyUsedException.java b/src/main/java/com/google/gerrit/client/rpc/NameAlreadyUsedException.java
deleted file mode 100644
index 75ef431537..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/NameAlreadyUsedException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating entity name is already taken by another entity. */
-public class NameAlreadyUsedException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Name Already Used";
-
- public NameAlreadyUsedException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/NoSuchAccountException.java b/src/main/java/com/google/gerrit/client/rpc/NoSuchAccountException.java
deleted file mode 100644
index bc3255ed5a..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/NoSuchAccountException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the account requested doesn't exist. */
-public class NoSuchAccountException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Found: ";
-
- public NoSuchAccountException(String who) {
- super(MESSAGE + who);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/NoSuchEntityException.java b/src/main/java/com/google/gerrit/client/rpc/NoSuchEntityException.java
deleted file mode 100644
index bcd271d30e..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/NoSuchEntityException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error indicating the entity requested doesn't exist. */
-public class NoSuchEntityException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Found";
-
- public NoSuchEntityException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/NotSignedInException.java b/src/main/java/com/google/gerrit/client/rpc/NotSignedInException.java
deleted file mode 100644
index 2cadef566b..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/NotSignedInException.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-/** Error stating the user must be signed-in in order to perform this action. */
-public class NotSignedInException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public static final String MESSAGE = "Not Signed In";
-
- public NotSignedInException() {
- super(MESSAGE);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java b/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
deleted file mode 100644
index e5f36af159..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/ScreenLoadCallback.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.NotFoundScreen;
-import com.google.gerrit.client.ui.Screen;
-
-/** Callback switching {@link NoSuchEntityException} to {@link NotFoundScreen} */
-public abstract class ScreenLoadCallback<T> extends GerritCallback<T> {
- private final Screen screen;
-
- public ScreenLoadCallback(final Screen s) {
- screen = s;
- }
-
- public final void onSuccess(final T result) {
- if (screen.isAttached()) {
- preDisplay(result);
- screen.display();
- postDisplay();
- }
- }
-
- protected abstract void preDisplay(T result);
-
- protected void postDisplay() {
- }
-
- @Override
- public void onFailure(final Throwable caught) {
- if (isNoSuchEntity(caught)) {
- Gerrit.display(new NotFoundScreen());
- } else {
- super.onFailure(caught);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/rpc/SignInRequired.java b/src/main/java/com/google/gerrit/client/rpc/SignInRequired.java
deleted file mode 100644
index 7fdbb6f60d..0000000000
--- a/src/main/java/com/google/gerrit/client/rpc/SignInRequired.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.rpc;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation indicating a service method requires a current user.
- * <p>
- * If there is no current user then {@link NotSignedInException} will be given
- * to the callback's onFailure method. Methods tagged with this will never get
- * null from {@link Common#getAccountId()}.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface SignInRequired {
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java b/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java
deleted file mode 100644
index 162be008be..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/AccountDashboardLink.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.AccountDashboardScreen;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Link to any user's account dashboard. */
-public class AccountDashboardLink extends DirectScreenLink {
- /** Create a link after locating account details from an active cache. */
- public static AccountDashboardLink link(final AccountInfoCache cache,
- final Account.Id id) {
- final AccountInfo ai = cache.get(id);
- return ai != null ? new AccountDashboardLink(ai) : null;
- }
-
- private Account.Id accountId;
-
- public AccountDashboardLink(final AccountInfo ai) {
- this(FormatUtil.name(ai), ai);
- }
-
- public AccountDashboardLink(final String text, final AccountInfo ai) {
- this(text, ai.getId());
- setTitle(FormatUtil.nameEmail(ai));
- }
-
- public AccountDashboardLink(final String text, final Account.Id ai) {
- super(text, Link.toAccountDashboard(ai));
- addStyleName("gerrit-AccountName");
- accountId = ai;
- }
-
- @Override
- protected Screen createScreen() {
- return new AccountDashboardScreen(accountId);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
deleted file mode 100644
index e0a497a9be..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.RpcStatus;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Suggestion Oracle for AccountGroup entities. */
-public class AccountGroupSuggestOracle extends HighlightSuggestOracle {
- @Override
- public void onRequestSuggestions(final Request req, final Callback callback) {
- RpcStatus.hide(new Runnable() {
- public void run() {
- SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(),
- new GerritCallback<List<AccountGroup>>() {
- public void onSuccess(final List<AccountGroup> result) {
- final ArrayList<AccountGroupSuggestion> r =
- new ArrayList<AccountGroupSuggestion>(result.size());
- for (final AccountGroup p : result) {
- r.add(new AccountGroupSuggestion(p));
- }
- callback.onSuggestionsReady(req, new Response(r));
- }
- });
- }
- });
- }
-
- private static class AccountGroupSuggestion implements
- SuggestOracle.Suggestion {
- private final AccountGroup info;
-
- AccountGroupSuggestion(final AccountGroup k) {
- info = k;
- }
-
- public String getDisplayString() {
- return info.getName();
- }
-
- public String getReplacementString() {
- return info.getName();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java b/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
deleted file mode 100644
index 51da314f37..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/AccountSuggestOracle.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.RpcStatus;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Suggestion Oracle for Account entities. */
-public class AccountSuggestOracle extends HighlightSuggestOracle {
- @Override
- public void onRequestSuggestions(final Request req, final Callback callback) {
- RpcStatus.hide(new Runnable() {
- public void run() {
- SuggestUtil.SVC.suggestAccount(req.getQuery(), req.getLimit(),
- new GerritCallback<List<AccountInfo>>() {
- public void onSuccess(final List<AccountInfo> result) {
- final ArrayList<AccountSuggestion> r =
- new ArrayList<AccountSuggestion>(result.size());
- for (final AccountInfo p : result) {
- r.add(new AccountSuggestion(p));
- }
- callback.onSuggestionsReady(req, new Response(r));
- }
- });
- }
- });
- }
-
- private static class AccountSuggestion implements SuggestOracle.Suggestion {
- private final AccountInfo info;
-
- AccountSuggestion(final AccountInfo k) {
- info = k;
- }
-
- public String getDisplayString() {
- return FormatUtil.nameEmail(info);
- }
-
- public String getReplacementString() {
- return FormatUtil.nameEmail(info);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/ChangeLink.java b/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
deleted file mode 100644
index 925377e4de..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/ChangeLink.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.ChangeScreen;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.DOM;
-
-public class ChangeLink extends DirectScreenLink {
- public static String permalink(final Change.Id c) {
- return GWT.getHostPageBaseURL() + c.get();
- }
-
- protected Change.Id id;
- private ChangeInfo info;
-
- public ChangeLink(final String text, final Change.Id c) {
- super(text, Link.toChange(c));
- DOM.setElementProperty(getElement(), "href", permalink(c));
- id = c;
- }
-
- public ChangeLink(final String text, final ChangeInfo c) {
- this(text, c.getId());
- info = c;
- }
-
- @Override
- protected Screen createScreen() {
- return info != null ? new ChangeScreen(info) : new ChangeScreen(id);
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/CommentPanel.java b/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
deleted file mode 100644
index d7ea85fbac..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/CommentPanel.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.FormatUtil;
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.DoubleClickEvent;
-import com.google.gwt.event.dom.client.DoubleClickHandler;
-import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
-import com.google.gwt.event.shared.HandlerRegistration;
-import com.google.gwt.user.client.ui.Composite;
-import com.google.gwt.user.client.ui.FlexTable;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.HTML;
-import com.google.gwt.user.client.ui.HasHorizontalAlignment;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
-import com.google.gwtexpui.safehtml.client.SafeHtml;
-import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-
-import java.util.Date;
-
-public class CommentPanel extends Composite implements HasDoubleClickHandlers {
- private static final int SUMMARY_LENGTH = 75;
- private final FlexTable header;
- private final InlineLabel messageSummary;
- private final FlowPanel content;
- private final DoubleClickHTML messageText;
- private FlowPanel buttons;
- private boolean recent;
-
- public CommentPanel(final AccountInfo author, final Date when, String message) {
- this();
-
- setMessageText(message);
- setAuthorNameText(FormatUtil.name(author));
- setDateText(FormatUtil.shortFormat(when));
-
- final CellFormatter fmt = header.getCellFormatter();
- fmt.getElement(0, 0).setTitle(FormatUtil.nameEmail(author));
- fmt.getElement(0, 2).setTitle(FormatUtil.mediumFormat(when));
- }
-
- protected CommentPanel() {
- final FlowPanel body = new FlowPanel();
- initWidget(body);
- setStyleName("gerrit-CommentPanel");
-
- messageSummary = new InlineLabel();
- messageSummary.setStyleName("gerrit-CommentPanel-Summary");
-
- header = new FlexTable();
- header.setStyleName("gerrit-CommentPanel-Header");
- header.addClickHandler(new ClickHandler() {
- @Override
- public void onClick(ClickEvent event) {
- setOpen(!isOpen());
- }
- });
- header.setText(0, 0, "");
- header.setWidget(0, 1, messageSummary);
- header.setText(0, 2, "");
- final CellFormatter fmt = header.getCellFormatter();
- fmt.setStyleName(0, 0, "gerrit-CommentPanel-AuthorCell");
- fmt.setStyleName(0, 1, "gerrit-CommentPanel-SummaryCell");
- fmt.setStyleName(0, 2, "gerrit-CommentPanel-DateCell");
- fmt.setHorizontalAlignment(0, 2, HasHorizontalAlignment.ALIGN_RIGHT);
- body.add(header);
-
- content = new FlowPanel();
- content.setStyleName("gerrit-CommentPanel-Content");
- content.setVisible(false);
- body.add(content);
-
- messageText = new DoubleClickHTML();
- messageText.setStyleName("gerrit-CommentPanel-Message");
- content.add(messageText);
- }
-
- @Override
- public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
- return messageText.addDoubleClickHandler(handler);
- }
-
- protected void setMessageText(String message) {
- if (message == null) {
- message = "";
- } else {
- message = message.trim();
- }
-
- messageSummary.setText(summarize(message));
- SafeHtml.set(messageText, new SafeHtmlBuilder().append(message).wikify()
- .replaceAll(Gerrit.getConfig().getCommentLinks()));
- }
-
- public void setAuthorNameText(final String nameText) {
- header.setText(0, 0, nameText);
- }
-
- protected void setDateText(final String dateText) {
- header.setText(0, 2, dateText);
- }
-
- protected void setMessageTextVisible(final boolean show) {
- messageText.setVisible(show);
- }
-
- protected void addContent(final Widget w) {
- if (buttons != null) {
- content.insert(w, content.getWidgetIndex(buttons));
- } else {
- content.add(w);
- }
- }
-
- protected Panel getButtonPanel() {
- if (buttons == null) {
- buttons = new FlowPanel();
- buttons.setStyleName("gerrit-CommentPanel-Buttons");
- content.add(buttons);
- }
- return buttons;
- }
-
- private static String summarize(final String message) {
- if (message.length() < SUMMARY_LENGTH) {
- return message;
- }
-
- int p = 0;
- final StringBuilder r = new StringBuilder();
- while (r.length() < SUMMARY_LENGTH) {
- final int e = message.indexOf(' ', p);
- if (e < 0) {
- break;
- }
-
- final String word = message.substring(p, e).trim();
- if (SUMMARY_LENGTH <= r.length() + word.length() + 1) {
- break;
- }
- if (r.length() > 0) {
- r.append(' ');
- }
- r.append(word);
- p = e + 1;
- }
- r.append(" \u2026");
- return r.toString();
- }
-
- public boolean isOpen() {
- return content.isVisible();
- }
-
- public void setOpen(final boolean open) {
- messageSummary.setVisible(!open);
- content.setVisible(open);
- }
-
- public boolean isRecent() {
- return recent;
- }
-
- public void setRecent(final boolean r) {
- recent = r;
- }
-
- private static class DoubleClickHTML extends HTML implements
- HasDoubleClickHandlers {
- public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
- return addDomHandler(handler, DoubleClickEvent.getType());
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/PatchLink.java b/src/main/java/com/google/gerrit/client/ui/PatchLink.java
deleted file mode 100644
index 9273f26102..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/PatchLink.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.PatchTable;
-import com.google.gerrit.client.patches.PatchScreen;
-import com.google.gerrit.client.reviewdb.Patch;
-
-public abstract class PatchLink extends DirectScreenLink {
- protected Patch.Key patchKey;
- protected int patchIndex;
- protected PatchTable parentPatchTable;
-
- /**
- * @param text The text of this link
- * @param patchKey The key for this patch
- * @param patchIndex The index of the current patch in the patch set
- * @param historyToken The history token
- * @param parentPatchTable The table used to display this link
- */
- public PatchLink(final String text, final Patch.Key patchKey, final int patchIndex,
- final String historyToken, PatchTable parentPatchTable) {
- super(text, historyToken);
- this.patchKey = patchKey;
- this.patchIndex = patchIndex;
- this.parentPatchTable = parentPatchTable;
- }
-
- public static class SideBySide extends PatchLink {
- public SideBySide(final String text, final Patch.Key patchKey, final int patchIndex,
- PatchTable parentPatchTable) {
- super(text, patchKey, patchIndex, Link.toPatchSideBySide(patchKey), parentPatchTable);
- }
-
- @Override
- protected Screen createScreen() {
- return new PatchScreen.SideBySide(patchKey, patchIndex, parentPatchTable);
- }
- }
-
- public static class Unified extends PatchLink {
- public Unified(final String text, final Patch.Key patchKey, final int patchIndex,
- PatchTable parentPatchTable) {
- super(text, patchKey, patchIndex, Link.toPatchUnified(patchKey), parentPatchTable);
- }
-
- @Override
- protected Screen createScreen() {
- return new PatchScreen.Unified(patchKey, patchIndex, parentPatchTable);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/ProjectLink.java b/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
deleted file mode 100644
index d743c5f65f..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/ProjectLink.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.changes.ByProjectAbandonedChangesScreen;
-import com.google.gerrit.client.changes.ByProjectMergedChangesScreen;
-import com.google.gerrit.client.changes.ByProjectOpenChangesScreen;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.Change.Status;
-
-/** Link to the open changes of a project. */
-public class ProjectLink extends DirectScreenLink {
- private Project.NameKey project;
- private Status status;
-
- public ProjectLink(final Project.NameKey proj, Change.Status stat) {
- this(proj.get(), proj, stat);
- }
-
- public ProjectLink(final String text, final Project.NameKey proj,
- Change.Status stat) {
- super(text, Link.toProject(proj, stat));
- status = stat;
- project = proj;
- }
-
- @Override
- protected Screen createScreen() {
- switch (status) {
- case ABANDONED:
- return new ByProjectAbandonedChangesScreen(project, "n,z");
-
- case MERGED:
- return new ByProjectMergedChangesScreen(project, "n,z");
-
- case NEW:
- case SUBMITTED:
- default:
- return new ByProjectOpenChangesScreen(project, "n,z");
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java b/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
deleted file mode 100644
index 86195490aa..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/ProjectNameSuggestOracle.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.RpcStatus;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gwt.user.client.ui.SuggestOracle;
-import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Suggestion Oracle for Project.NameKey entities. */
-public class ProjectNameSuggestOracle extends HighlightSuggestOracle {
- @Override
- public void onRequestSuggestions(final Request req, final Callback callback) {
- RpcStatus.hide(new Runnable() {
- public void run() {
- SuggestUtil.SVC.suggestProjectNameKey(req.getQuery(), req.getLimit(),
- new GerritCallback<List<Project.NameKey>>() {
- public void onSuccess(final List<Project.NameKey> result) {
- final ArrayList<ProjectNameSuggestion> r =
- new ArrayList<ProjectNameSuggestion>(result.size());
- for (final Project.NameKey p : result) {
- r.add(new ProjectNameSuggestion(p));
- }
- callback.onSuggestionsReady(req, new Response(r));
- }
- });
- }
- });
- }
-
- private static class ProjectNameSuggestion implements
- SuggestOracle.Suggestion {
- private final Project.NameKey key;
-
- ProjectNameSuggestion(final Project.NameKey k) {
- key = k;
- }
-
- public String getDisplayString() {
- return key.get();
- }
-
- public String getReplacementString() {
- return key.get();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/Screen.java b/src/main/java/com/google/gerrit/client/ui/Screen.java
deleted file mode 100644
index e6f8372a20..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/Screen.java
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.Link;
-import com.google.gwt.user.client.History;
-import com.google.gwt.user.client.ui.FlowPanel;
-import com.google.gwt.user.client.ui.InlineLabel;
-import com.google.gwt.user.client.ui.Widget;
-import com.google.gwtexpui.user.client.View;
-
-public abstract class Screen extends View {
- private FlowPanel header;
- private InlineLabel headerText;
- private FlowPanel body;
- private boolean requiresSignIn;
- private String windowTitle;
-
- protected Screen() {
- initWidget(new FlowPanel());
- setStyleName("gerrit-Screen");
- }
-
- @Override
- protected void onLoad() {
- super.onLoad();
- if (header == null) {
- onInitUI();
- }
- }
-
- public void registerKeys() {
- }
-
- protected void onInitUI() {
- final FlowPanel me = (FlowPanel) getWidget();
- me.add(header = new FlowPanel());
- me.add(body = new FlowPanel());
-
- header.setStyleName("gerrit-ScreenHeader");
- header.add(headerText = new InlineLabel());
- }
-
- protected void setWindowTitle(final String text) {
- windowTitle = text;
- Gerrit.setWindowTitle(this, text);
- }
-
- protected void setPageTitle(final String text) {
- final String old = headerText.getText();
- headerText.setText(text);
- if (windowTitle == null || windowTitle == old) {
- setWindowTitle(text);
- }
- }
-
- protected void insertTitleWidget(final Widget w) {
- header.insert(w, 0);
- }
-
- protected final void add(final Widget w) {
- body.add(w);
- }
-
- /** Set whether or not {@link Gerrit#isSignedIn()} must be true. */
- public final void setRequiresSignIn(final boolean b) {
- requiresSignIn = b;
- }
-
- /** Does {@link Gerrit#isSignedIn()} have to be true to be on this screen? */
- public final boolean isRequiresSignIn() {
- return requiresSignIn;
- }
-
- /** Invoked if this screen is the current screen and the user signs out. */
- public void onSignOut() {
- if (isRequiresSignIn()) {
- History.newItem(Link.ALL_OPEN);
- }
- }
-
- public void onShowView() {
- if (windowTitle != null) {
- Gerrit.setWindowTitle(this, windowTitle);
- }
- registerKeys();
- }
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/SuggestService.java b/src/main/java/com/google/gerrit/client/ui/SuggestService.java
deleted file mode 100644
index dd841652c7..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/SuggestService.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-
-import java.util.List;
-
-public interface SuggestService extends RemoteJsonService {
- void suggestProjectNameKey(String query, int limit,
- AsyncCallback<List<Project.NameKey>> callback);
-
- void suggestAccount(String query, int limit,
- AsyncCallback<List<AccountInfo>> callback);
-
- void suggestAccountGroup(String query, int limit,
- AsyncCallback<List<AccountGroup>> callback);
-}
diff --git a/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java b/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java
deleted file mode 100644
index 81d11c4f83..0000000000
--- a/src/main/java/com/google/gerrit/client/ui/SuggestUtil.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.client.ui;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwtjsonrpc.client.JsonUtil;
-
-public class SuggestUtil {
- public static final SuggestService SVC;
-
- static {
- SVC = GWT.create(SuggestService.class);
- JsonUtil.bind(SVC, "rpc/SuggestService");
- }
-
- private SuggestUtil() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/ChangeMergeQueue.java b/src/main/java/com/google/gerrit/git/ChangeMergeQueue.java
deleted file mode 100644
index 0e7c8d529e..0000000000
--- a/src/main/java/com/google/gerrit/git/ChangeMergeQueue.java
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.inject.Inject;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-public class ChangeMergeQueue implements MergeQueue {
- private static final Logger log =
- LoggerFactory.getLogger(ChangeMergeQueue.class);
-
- private final Map<Branch.NameKey, MergeEntry> active =
- new HashMap<Branch.NameKey, MergeEntry>();
- private final Map<Branch.NameKey, RecheckJob> recheck =
- new HashMap<Branch.NameKey, RecheckJob>();
-
- private final WorkQueue workQueue;
- private final MergeOp.Factory opFactory;
-
- @Inject
- ChangeMergeQueue(final WorkQueue wq, final MergeOp.Factory of) {
- workQueue = wq;
- opFactory = of;
- }
-
- @Override
- public void merge(final Branch.NameKey branch) {
- if (start(branch)) {
- mergeImpl(branch);
- }
- }
-
- private synchronized boolean start(final Branch.NameKey branch) {
- final MergeEntry e = active.get(branch);
- if (e == null) {
- // Let the caller attempt this merge, its the only one interested
- // in processing this branch right now.
- //
- active.put(branch, new MergeEntry(branch));
- return true;
- } else {
- // Request that the job queue handle this merge later.
- //
- e.needMerge = true;
- return false;
- }
- }
-
- @Override
- public synchronized void schedule(final Branch.NameKey branch) {
- MergeEntry e = active.get(branch);
- if (e == null) {
- e = new MergeEntry(branch);
- active.put(branch, e);
- }
- e.needMerge = true;
- scheduleJob(e);
- }
-
- @Override
- public synchronized void recheckAfter(final Branch.NameKey branch,
- final long delay, final TimeUnit delayUnit) {
- final long now = System.currentTimeMillis();
- final long at = now + MILLISECONDS.convert(delay, delayUnit);
- RecheckJob e = recheck.get(branch);
- if (e == null) {
- e = new RecheckJob(branch);
- workQueue.getDefaultQueue().schedule(e, now - at, MILLISECONDS);
- recheck.put(branch, e);
- }
- e.recheckAt = Math.max(at, e.recheckAt);
- }
-
- private synchronized void finish(final Branch.NameKey branch) {
- final MergeEntry e = active.get(branch);
- if (e == null) {
- // Not registered? Shouldn't happen but ignore it.
- //
- return;
- }
-
- if (!e.needMerge) {
- // No additional merges are in progress, we can delete it.
- //
- active.remove(branch);
- return;
- }
-
- scheduleJob(e);
- }
-
- private void scheduleJob(final MergeEntry e) {
- if (!e.jobScheduled) {
- // No job has been scheduled to execute this branch, but it needs
- // to run a merge again.
- //
- e.jobScheduled = true;
- workQueue.getDefaultQueue().schedule(e, 0, TimeUnit.SECONDS);
- }
- }
-
- private synchronized void unschedule(final MergeEntry e) {
- e.jobScheduled = false;
- e.needMerge = false;
- }
-
- private void mergeImpl(final Branch.NameKey branch) {
- try {
- opFactory.create(branch).merge();
- } catch (Throwable e) {
- log.error("Merge attempt for " + branch + " failed", e);
- } finally {
- finish(branch);
- }
- }
-
- private synchronized void recheck(final RecheckJob e) {
- final long remainingDelay = e.recheckAt - System.currentTimeMillis();
- if (MILLISECONDS.convert(10, SECONDS) < remainingDelay) {
- // Woke up too early, the job deadline was pushed back.
- // Reschedule for the new deadline. We allow for a small
- // amount of fuzz due to multiple reschedule attempts in
- // a short period of time being caused by MergeOp.
- //
- workQueue.getDefaultQueue().schedule(e, remainingDelay, MILLISECONDS);
- } else {
- // Schedule a merge attempt on this branch to see if we can
- // actually complete it this time.
- //
- schedule(e.dest);
- }
- }
-
- private class MergeEntry implements Runnable {
- final Branch.NameKey dest;
- boolean needMerge;
- boolean jobScheduled;
-
- MergeEntry(final Branch.NameKey d) {
- dest = d;
- }
-
- public void run() {
- unschedule(this);
- mergeImpl(dest);
- }
-
- @Override
- public String toString() {
- final Project.NameKey project = dest.getParentKey();
- return "submit " + project.get() + " " + dest.getShortName();
- }
- }
-
- private class RecheckJob implements Runnable {
- final Branch.NameKey dest;
- long recheckAt;
-
- RecheckJob(final Branch.NameKey d) {
- dest = d;
- }
-
- @Override
- public void run() {
- recheck(this);
- }
-
- @Override
- public String toString() {
- final Project.NameKey project = dest.getParentKey();
- return "recheck " + project.get() + " " + dest.getShortName();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/CodeReviewCommit.java b/src/main/java/com/google/gerrit/git/CodeReviewCommit.java
deleted file mode 100644
index 0b67e43553..0000000000
--- a/src/main/java/com/google/gerrit/git/CodeReviewCommit.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-
-import java.util.List;
-
-/** Extended commit entity with code review specific metadata. */
-class CodeReviewCommit extends RevCommit {
- static CodeReviewCommit error(final CommitMergeStatus s) {
- final CodeReviewCommit r = new CodeReviewCommit(ObjectId.zeroId());
- r.statusCode = s;
- return r;
- }
-
- /**
- * Unique key of the PatchSet entity from the code review system.
- * <p>
- * This value is only available on commits that have a PatchSet represented in
- * the code review system.
- */
- PatchSet.Id patchsetId;
-
- /** The change containing {@link #patchsetId} . */
- Change change;
-
- /**
- * Ordinal position of this commit within the submit queue.
- * <p>
- * Only valid if {@link #patchsetId} is not null.
- */
- int originalOrder;
-
- /**
- * The result status for this commit.
- * <p>
- * Only valid if {@link #patchsetId} is not null.
- */
- CommitMergeStatus statusCode;
-
- /** Commits which are missing ancestors of this commit. */
- List<CodeReviewCommit> missing;
-
- CodeReviewCommit(final AnyObjectId id) {
- super(id);
- }
-
- void copyFrom(final CodeReviewCommit src) {
- patchsetId = src.patchsetId;
- change = src.change;
- originalOrder = src.originalOrder;
- statusCode = src.statusCode;
- missing = src.missing;
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/CommitMergeStatus.java b/src/main/java/com/google/gerrit/git/CommitMergeStatus.java
deleted file mode 100644
index 665a0425b2..0000000000
--- a/src/main/java/com/google/gerrit/git/CommitMergeStatus.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-enum CommitMergeStatus {
- /** */
- CLEAN_MERGE,
-
- /** */
- CLEAN_PICK,
-
- /** */
- ALREADY_MERGED,
-
- /** */
- PATH_CONFLICT,
-
- /** */
- MISSING_DEPENDENCY,
-
- /** */
- NO_PATCH_SET,
-
- /** */
- REVISION_GONE,
-
- /** */
- CRISS_CROSS_MERGE,
-
- /** */
- CANNOT_CHERRY_PICK_ROOT,
-
- /** */
- NOT_FAST_FORWARD;
-}
diff --git a/src/main/java/com/google/gerrit/git/DefaultQueueOp.java b/src/main/java/com/google/gerrit/git/DefaultQueueOp.java
deleted file mode 100644
index 1f87b553ca..0000000000
--- a/src/main/java/com/google/gerrit/git/DefaultQueueOp.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import java.util.concurrent.TimeUnit;
-
-public abstract class DefaultQueueOp implements Runnable {
- private final WorkQueue workQueue;
-
- protected DefaultQueueOp(final WorkQueue wq) {
- workQueue = wq;
- }
-
- public void start(final int delay, final TimeUnit unit) {
- workQueue.getDefaultQueue().schedule(this, delay, unit);
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/GitRepositoryManager.java b/src/main/java/com/google/gerrit/git/GitRepositoryManager.java
deleted file mode 100644
index a278501a6a..0000000000
--- a/src/main/java/com/google/gerrit/git/GitRepositoryManager.java
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.LockFile;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.lib.RepositoryCache;
-import org.eclipse.jgit.lib.RepositoryCache.FileKey;
-
-import java.io.File;
-import java.io.IOException;
-
-/** Class managing Git repositories. */
-@Singleton
-public class GitRepositoryManager {
- private static final Logger log = LoggerFactory.getLogger(GitRepositoryManager.class);
- private final File sitePath;
- private final File basepath;
-
- @Inject
- GitRepositoryManager(@SitePath final File path, @GerritServerConfig final Config cfg) {
- sitePath = path;
-
- final String basePath = cfg.getString("gerrit", null, "basepath");
- if (basePath != null) {
- File root = new File(basePath);
- if (!root.isAbsolute()) {
- root = new File(sitePath, basePath);
- }
- basepath = root;
- } else {
- basepath = null;
- }
- }
-
- /**
- * Get (or open) a repository by name.
- *
- * @param name the repository name, relative to the base directory.
- * @return the cached Repository instance. Caller must call {@code close()}
- * when done to decrement the resource handle.
- * @throws RepositoryNotFoundException the name does not denote an existing
- * repository, or the name cannot be read as a repository.
- */
- public Repository openRepository(String name)
- throws RepositoryNotFoundException {
- if (basepath == null) {
- throw new RepositoryNotFoundException("No gerrit.basepath configured");
- }
-
- if (isUnreasonableName(name)) {
- throw new RepositoryNotFoundException("Invalid name: " + name);
- }
-
- try {
- final FileKey loc = FileKey.lenient(new File(basepath, name));
- return RepositoryCache.open(loc);
- } catch (IOException e1) {
- final RepositoryNotFoundException e2;
- e2 = new RepositoryNotFoundException("Cannot open repository " + name);
- e2.initCause(e1);
- throw e2;
- }
- }
-
- /**
- * Create (and open) a repository by name.
- *
- * @param name the repository name, relative to the base directory.
- * @return the cached Repository instance. Caller must call {@code close()}
- * when done to decrement the resource handle.
- * @throws RepositoryNotFoundException the name does not denote an existing
- * repository, or the name cannot be read as a repository.
- */
- public Repository createRepository(String name)
- throws RepositoryNotFoundException {
- if (basepath == null) {
- throw new RepositoryNotFoundException("No gerrit.basepath configured");
- }
-
- if (isUnreasonableName(name)) {
- throw new RepositoryNotFoundException("Invalid name: " + name);
- }
-
- try {
- if (!name.endsWith(".git")) {
- name = name + ".git";
- }
- final FileKey loc = FileKey.exact(new File(basepath, name));
- return RepositoryCache.open(loc, false);
- } catch (IOException e1) {
- final RepositoryNotFoundException e2;
- e2 = new RepositoryNotFoundException("Cannot open repository " + name);
- e2.initCause(e1);
- throw e2;
- }
- }
-
- /**
- * Set the {@code GIT_DIR/description} file for gitweb.
- * <p>
- * NB: This code should really be in JGit, as a member of the Repostiory
- * object. Until it moves there, its here.
- *
- * @param name the repository name, relative to the base directory.
- * @param description new description text for the repository.
- */
- public void setProjectDescription(final String name, final String description) {
- // Update git's description file, in case gitweb is being used
- //
- try {
- final Repository e;
- final LockFile f;
-
- e = openRepository(name);
- f = new LockFile(new File(e.getDirectory(), "description"));
- if (f.lock()) {
- String d = description;
- if (d != null) {
- d = d.trim();
- if (d.length() > 0) {
- d += "\n";
- }
- } else {
- d = "";
- }
- f.write(Constants.encode(d));
- f.commit();
- }
- e.close();
- } catch (RepositoryNotFoundException e) {
- log.error("Cannot update description for " + name, e);
- } catch (IOException e) {
- log.error("Cannot update description for " + name, e);
- }
- }
-
- private boolean isUnreasonableName(final String name) {
- if (name.length() == 0) return true; // no empty paths
-
- if (name.indexOf('\\') >= 0) return true; // no windows/dos stlye paths
- if (name.charAt(0) == '/') return true; // no absolute paths
- if (new File(name).isAbsolute()) return true; // no absolute paths
-
- if (name.startsWith("../")) return true; // no "l../etc/passwd"
- if (name.contains("/../")) return true; // no "foo/../etc/passwd"
- if (name.contains("/./")) return true; // "foo/./foo" is insane to ask
- if (name.contains("//")) return true; // windows UNC path can be "//..."
-
- return false; // is a reasonable name
- }
-
-}
diff --git a/src/main/java/com/google/gerrit/git/MergeException.java b/src/main/java/com/google/gerrit/git/MergeException.java
deleted file mode 100644
index 902e296828..0000000000
--- a/src/main/java/com/google/gerrit/git/MergeException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-/** Indicates the current branch's queue cannot be processed at this time. */
-class MergeException extends Exception {
- private static final long serialVersionUID = 1L;
-
- MergeException(final String msg) {
- super(msg, null);
- }
-
- MergeException(final String msg, final Throwable why) {
- super(msg, why);
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/MergeOp.java b/src/main/java/com/google/gerrit/git/MergeOp.java
deleted file mode 100644
index d2675d7e44..0000000000
--- a/src/main/java/com/google/gerrit/git/MergeOp.java
+++ /dev/null
@@ -1,1189 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.MINUTES;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.mail.MergeFailSender;
-import com.google.gerrit.server.mail.MergedSender;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.workflow.CategoryFunction;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.assistedinject.Assisted;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.Commit;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.merge.MergeStrategy;
-import org.eclipse.jgit.merge.Merger;
-import org.eclipse.jgit.merge.ThreeWayMerger;
-import org.eclipse.jgit.revwalk.FooterKey;
-import org.eclipse.jgit.revwalk.FooterLine;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Merges changes in submission order into a single branch.
- * <p>
- * Branches are reduced to the minimum number of heads needed to merge
- * everything. This allows commits to be entered into the queue in any order
- * (such as ancestors before descendants) and only the most recent commit on any
- * line of development will be merged. All unmerged commits along a line of
- * development must be in the submission queue in order to merge the tip of that
- * line.
- * <p>
- * Conflicts are handled by discarding the entire line of development and
- * marking it as conflicting, even if an earlier commit along that same line can
- * be merged cleanly.
- */
-public class MergeOp {
- public interface Factory {
- MergeOp create(Branch.NameKey branch);
- }
-
- private static final Logger log = LoggerFactory.getLogger(MergeOp.class);
- private static final String R_HEADS_MASTER =
- Constants.R_HEADS + Constants.MASTER;
- private static final ApprovalCategory.Id CRVW =
- new ApprovalCategory.Id("CRVW");
- private static final ApprovalCategory.Id VRIF =
- new ApprovalCategory.Id("VRIF");
- private static final FooterKey REVIEWED_ON = new FooterKey("Reviewed-on");
- private static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
-
- /** Amount of time to wait between submit and checking for missing deps. */
- private static final long DEPENDENCY_DELAY =
- MILLISECONDS.convert(15, MINUTES);
-
- private final GitRepositoryManager repoManager;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final ProjectCache projectCache;
- private final FunctionState.Factory functionState;
- private final ReplicationQueue replication;
- private final MergedSender.Factory mergedSenderFactory;
- private final MergeFailSender.Factory mergeFailSenderFactory;
- private final Provider<String> urlProvider;
- private final ApprovalTypes approvalTypes;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final IdentifiedUser.GenericFactory identifiedUserFactory;
- private final MergeQueue mergeQueue;
-
- private final PersonIdent myIdent;
- private final Branch.NameKey destBranch;
- private Project destProject;
- private final List<CodeReviewCommit> toMerge;
- private List<Change> submitted;
- private final Map<Change.Id, CodeReviewCommit> commits;
- private ReviewDb schema;
- private Repository db;
- private RevWalk rw;
- private RevFlag CAN_MERGE;
- private CodeReviewCommit branchTip;
- private CodeReviewCommit mergeTip;
- private Set<RevCommit> alreadyAccepted;
- private RefUpdate branchUpdate;
-
- @Inject
- MergeOp(final GitRepositoryManager grm, final SchemaFactory<ReviewDb> sf,
- final ProjectCache pc, final FunctionState.Factory fs,
- final ReplicationQueue rq, final MergedSender.Factory msf,
- final MergeFailSender.Factory mfsf,
- @CanonicalWebUrl @Nullable final Provider<String> cwu,
- final ApprovalTypes approvalTypes, final PatchSetInfoFactory psif,
- final IdentifiedUser.GenericFactory iuf,
- @GerritPersonIdent final PersonIdent myIdent,
- final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch) {
- repoManager = grm;
- schemaFactory = sf;
- functionState = fs;
- projectCache = pc;
- replication = rq;
- mergedSenderFactory = msf;
- mergeFailSenderFactory = mfsf;
- urlProvider = cwu;
- this.approvalTypes = approvalTypes;
- patchSetInfoFactory = psif;
- identifiedUserFactory = iuf;
- this.mergeQueue = mergeQueue;
-
- this.myIdent = myIdent;
- destBranch = branch;
- toMerge = new ArrayList<CodeReviewCommit>();
- commits = new HashMap<Change.Id, CodeReviewCommit>();
- }
-
- public void merge() throws MergeException {
- final ProjectState pe = projectCache.get(destBranch.getParentKey());
- if (pe == null) {
- throw new MergeException("No such project: " + destBranch.getParentKey());
- }
- destProject = pe.getProject();
-
- try {
- schema = schemaFactory.open();
- } catch (OrmException e) {
- throw new MergeException("Cannot open database", e);
- }
- try {
- mergeImpl();
- } finally {
- if (db != null) {
- db.close();
- }
- schema.close();
- schema = null;
- }
- }
-
- private void mergeImpl() throws MergeException {
- openRepository();
- openBranch();
- listPendingSubmits();
- validateChangeList();
- mergeTip = branchTip;
- switch (destProject.getSubmitType()) {
- case CHERRY_PICK:
- cherryPickChanges();
- break;
-
- case FAST_FORWARD_ONLY:
- case MERGE_ALWAYS:
- case MERGE_IF_NECESSARY:
- default:
- reduceToMinimalMerge();
- mergeTopics();
- markCleanMerges();
- break;
- }
- updateBranch();
- updateChangeStatus();
- }
-
- private void openRepository() throws MergeException {
- final String name = destBranch.getParentKey().get();
- try {
- db = repoManager.openRepository(name);
- } catch (RepositoryNotFoundException notGit) {
- final String m = "Repository \"" + name + "\" unknown.";
- throw new MergeException(m, notGit);
- }
-
- rw = new RevWalk(db) {
- @Override
- protected RevCommit createCommit(final AnyObjectId id) {
- return new CodeReviewCommit(id);
- }
- };
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.COMMIT_TIME_DESC, true);
- CAN_MERGE = rw.newFlag("CAN_MERGE");
- }
-
- private void openBranch() throws MergeException {
- alreadyAccepted = new HashSet<RevCommit>();
-
- try {
- branchUpdate = db.updateRef(destBranch.get());
- if (branchUpdate.getOldObjectId() != null) {
- branchTip =
- (CodeReviewCommit) rw.parseCommit(branchUpdate.getOldObjectId());
- alreadyAccepted.add(branchTip);
- } else {
- branchTip = null;
- }
-
- for (final Ref r : rw.getRepository().getAllRefs().values()) {
- if (r.getName().startsWith(Constants.R_HEADS)
- || r.getName().startsWith(Constants.R_TAGS)) {
- try {
- alreadyAccepted.add(rw.parseCommit(r.getObjectId()));
- } catch (IncorrectObjectTypeException iote) {
- // Not a commit? Skip over it.
- }
- }
- }
- } catch (IOException e) {
- throw new MergeException("Cannot open branch", e);
- }
- }
-
- private void listPendingSubmits() throws MergeException {
- try {
- submitted = schema.changes().submitted(destBranch).toList();
- } catch (OrmException e) {
- throw new MergeException("Cannot query the database", e);
- }
- }
-
- private void validateChangeList() throws MergeException {
- final Set<ObjectId> tips = new HashSet<ObjectId>();
- for (final Ref r : db.getAllRefs().values()) {
- tips.add(r.getObjectId());
- }
-
- int commitOrder = 0;
- for (final Change chg : submitted) {
- final Change.Id changeId = chg.getId();
- if (chg.currentPatchSetId() == null) {
- commits.put(changeId, CodeReviewCommit
- .error(CommitMergeStatus.NO_PATCH_SET));
- continue;
- }
-
- final PatchSet ps;
- try {
- ps = schema.patchSets().get(chg.currentPatchSetId());
- } catch (OrmException e) {
- throw new MergeException("Cannot query the database", e);
- }
- if (ps == null || ps.getRevision() == null
- || ps.getRevision().get() == null) {
- commits.put(changeId, CodeReviewCommit
- .error(CommitMergeStatus.NO_PATCH_SET));
- continue;
- }
-
- final String idstr = ps.getRevision().get();
- final ObjectId id;
- try {
- id = ObjectId.fromString(idstr);
- } catch (IllegalArgumentException iae) {
- commits.put(changeId, CodeReviewCommit
- .error(CommitMergeStatus.NO_PATCH_SET));
- continue;
- }
-
- if (!tips.contains(id)) {
- // TODO Technically the proper way to do this test is to use a
- // RevWalk on "$id --not --all" and test for an empty set. But
- // that is way slower than looking for a ref directly pointing
- // at the desired tip. We should always have a ref available.
- //
- // TODO this is actually an error, the branch is gone but we
- // want to merge the issue. We can't safely do that if the
- // tip is not reachable.
- //
- commits.put(changeId, CodeReviewCommit
- .error(CommitMergeStatus.REVISION_GONE));
- continue;
- }
-
- final CodeReviewCommit commit;
- try {
- commit = (CodeReviewCommit) rw.parseCommit(id);
- } catch (IOException e) {
- log.error("Invalid commit " + id.name() + " on " + chg.getKey(), e);
- commits.put(changeId, CodeReviewCommit
- .error(CommitMergeStatus.REVISION_GONE));
- continue;
- }
-
- commit.change = chg;
- commit.patchsetId = ps.getId();
- commit.originalOrder = commitOrder++;
- commits.put(changeId, commit);
-
- if (branchTip != null) {
- // If this commit is already merged its a bug in the queuing code
- // that we got back here. Just mark it complete and move on. Its
- // merged and that is all that mattered to the requestor.
- //
- try {
- if (rw.isMergedInto(commit, branchTip)) {
- commit.statusCode = CommitMergeStatus.ALREADY_MERGED;
- continue;
- }
- } catch (IOException err) {
- throw new MergeException("Cannot perform merge base test", err);
- }
- }
-
- commit.add(CAN_MERGE);
- toMerge.add(commit);
- }
- }
-
- private void reduceToMinimalMerge() throws MergeException {
- final Collection<CodeReviewCommit> heads;
- try {
- heads = new MergeSorter(rw, alreadyAccepted, CAN_MERGE).sort(toMerge);
- } catch (IOException e) {
- throw new MergeException("Branch head sorting failed", e);
- }
-
- toMerge.clear();
- toMerge.addAll(heads);
- Collections.sort(toMerge, new Comparator<CodeReviewCommit>() {
- public int compare(final CodeReviewCommit a, final CodeReviewCommit b) {
- return a.originalOrder - b.originalOrder;
- }
- });
- }
-
- private void mergeTopics() throws MergeException {
- // Take the first fast-forward available, if any is available in the set.
- //
- if (destProject.getSubmitType() != Project.SubmitType.MERGE_ALWAYS) {
- for (final Iterator<CodeReviewCommit> i = toMerge.iterator(); i.hasNext();) {
- try {
- final CodeReviewCommit n = i.next();
- if (mergeTip == null || rw.isMergedInto(mergeTip, n)) {
- mergeTip = n;
- i.remove();
- break;
- }
- } catch (IOException e) {
- throw new MergeException("Cannot fast-forward test during merge", e);
- }
- }
- }
-
- if (destProject.getSubmitType() == Project.SubmitType.FAST_FORWARD_ONLY) {
- // If this project only permits fast-forwards, abort everything else.
- //
- while (!toMerge.isEmpty()) {
- final CodeReviewCommit n = toMerge.remove(0);
- n.statusCode = CommitMergeStatus.NOT_FAST_FORWARD;
- }
-
- } else {
- // For every other commit do a pair-wise merge.
- //
- while (!toMerge.isEmpty()) {
- mergeOneCommit(toMerge.remove(0));
- }
- }
- }
-
- private void mergeOneCommit(final CodeReviewCommit n) throws MergeException {
- final Merger m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
- try {
- if (m.merge(new AnyObjectId[] {mergeTip, n})) {
- writeMergeCommit(m, n);
-
- } else {
- failed(n, CommitMergeStatus.PATH_CONFLICT);
- }
- } catch (IOException e) {
- if (e.getMessage().startsWith("Multiple merge bases for")) {
- try {
- failed(n, CommitMergeStatus.CRISS_CROSS_MERGE);
- } catch (IOException e2) {
- throw new MergeException("Cannot merge " + n.name(), e);
- }
- } else {
- throw new MergeException("Cannot merge " + n.name(), e);
- }
- }
- }
-
- private CodeReviewCommit failed(final CodeReviewCommit n,
- final CommitMergeStatus failure) throws MissingObjectException,
- IncorrectObjectTypeException, IOException {
- rw.reset();
- rw.markStart(n);
- rw.markUninteresting(mergeTip);
- CodeReviewCommit failed;
- while ((failed = (CodeReviewCommit) rw.next()) != null) {
- failed.statusCode = failure;
- }
- return failed;
- }
-
- private void writeMergeCommit(final Merger m, final CodeReviewCommit n)
- throws IOException, MissingObjectException, IncorrectObjectTypeException {
- final List<CodeReviewCommit> merged = new ArrayList<CodeReviewCommit>();
- rw.reset();
- rw.markStart(n);
- rw.markUninteresting(mergeTip);
- for (final RevCommit c : rw) {
- final CodeReviewCommit crc = (CodeReviewCommit) c;
- if (crc.patchsetId != null) {
- merged.add(crc);
- }
- }
-
- final StringBuilder msgbuf = new StringBuilder();
- if (merged.size() == 1) {
- final CodeReviewCommit c = merged.get(0);
- msgbuf.append("Merge change ");
- msgbuf.append(c.change.getKey().abbreviate());
- } else {
- final ArrayList<CodeReviewCommit> o;
- o = new ArrayList<CodeReviewCommit>(merged);
- Collections.sort(o, new Comparator<CodeReviewCommit>() {
- public int compare(final CodeReviewCommit a, final CodeReviewCommit b) {
- final Change.Id aId = a.patchsetId.getParentKey();
- final Change.Id bId = b.patchsetId.getParentKey();
- return aId.get() - bId.get();
- }
- });
-
- msgbuf.append("Merge changes ");
- for (final Iterator<CodeReviewCommit> i = o.iterator(); i.hasNext();) {
- msgbuf.append(i.next().change.getKey().abbreviate());
- if (i.hasNext()) {
- msgbuf.append(',');
- }
- }
- }
-
- if (!R_HEADS_MASTER.equals(destBranch.get())) {
- msgbuf.append(" into ");
- msgbuf.append(destBranch.getShortName());
- }
- msgbuf.append("\n\n* changes:\n");
- for (final CodeReviewCommit c : merged) {
- msgbuf.append(" ");
- msgbuf.append(c.getShortMessage());
- msgbuf.append("\n");
- }
-
- final Commit mergeCommit = new Commit(db);
- mergeCommit.setTreeId(m.getResultTreeId());
- mergeCommit.setParentIds(new ObjectId[] {mergeTip, n});
- mergeCommit.setAuthor(myIdent);
- mergeCommit.setCommitter(mergeCommit.getAuthor());
- mergeCommit.setMessage(msgbuf.toString());
-
- final ObjectId id = m.getObjectWriter().writeCommit(mergeCommit);
- mergeTip = (CodeReviewCommit) rw.parseCommit(id);
- }
-
- private void markCleanMerges() throws MergeException {
- if (mergeTip == null) {
- // If mergeTip is null here, branchTip was null, indicating a new branch
- // at the start of the merge process. We also elected to merge nothing,
- // probably due to missing dependencies. Nothing was cleanly merged.
- //
- return;
- }
-
- try {
- rw.reset();
- rw.sort(RevSort.TOPO);
- rw.sort(RevSort.REVERSE, true);
- rw.markStart(mergeTip);
- for (RevCommit c : alreadyAccepted) {
- rw.markUninteresting(c);
- }
-
- CodeReviewCommit c;
- while ((c = (CodeReviewCommit) rw.next()) != null) {
- if (c.patchsetId != null) {
- c.statusCode = CommitMergeStatus.CLEAN_MERGE;
- if (branchUpdate.getRefLogIdent() == null) {
- setRefLogIdent(getSubmitter(c.patchsetId));
- }
- }
- }
- } catch (IOException e) {
- throw new MergeException("Cannot mark clean merges", e);
- }
- }
-
- private void setRefLogIdent(final PatchSetApproval submitAudit) {
- if (submitAudit != null) {
- branchUpdate.setRefLogIdent(identifiedUserFactory.create(
- submitAudit.getAccountId()).newPersonIdent());
- }
- }
-
- private void cherryPickChanges() throws MergeException {
- while (!toMerge.isEmpty()) {
- final CodeReviewCommit n = toMerge.remove(0);
- final ThreeWayMerger m;
-
- m = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
- try {
- if (mergeTip == null) {
- // The branch is unborn. Take a fast-forward resolution to
- // create the branch.
- //
- mergeTip = n;
- n.statusCode = CommitMergeStatus.CLEAN_MERGE;
-
- } else if (n.getParentCount() == 0) {
- // Refuse to merge a root commit into an existing branch,
- // we cannot obtain a delta for the cherry-pick to apply.
- //
- n.statusCode = CommitMergeStatus.CANNOT_CHERRY_PICK_ROOT;
-
- } else if (n.getParentCount() == 1) {
- // If there is only one parent, a cherry-pick can be done by
- // taking the delta relative to that one parent and redoing
- // that on the current merge tip.
- //
- m.setBase(n.getParent(0));
- if (m.merge(mergeTip, n)) {
- writeCherryPickCommit(m, n);
-
- } else {
- n.statusCode = CommitMergeStatus.PATH_CONFLICT;
- }
-
- } else {
- // There are multiple parents, so this is a merge commit. We
- // don't want to cherry-pick the merge as clients can't easily
- // rebase their history with that merge present and replaced
- // by an equivalent merge with a different first parent. So
- // instead behave as though MERGE_IF_NECESSARY was configured.
- //
- if (hasDependenciesMet(n)) {
- if (rw.isMergedInto(mergeTip, n)) {
- mergeTip = n;
- } else {
- mergeOneCommit(n);
- }
- markCleanMerges();
-
- } else {
- // One or more dependencies were not met. The status was
- // already marked on the commit so we have nothing further
- // to perform at this time.
- //
- }
- }
-
- } catch (IOException e) {
- throw new MergeException("Cannot merge " + n.name(), e);
- }
- }
- }
-
- private boolean hasDependenciesMet(final CodeReviewCommit n)
- throws IOException {
- // Oddly we can determine this by running the merge sorter and
- // look for the one commit to come out as a result. This works
- // as the merge sorter checks the dependency chain as part of
- // its logic trying to find a minimal merge path.
- //
- return new MergeSorter(rw, alreadyAccepted, CAN_MERGE).sort(
- Collections.singleton(n)).contains(n);
- }
-
- private void writeCherryPickCommit(final Merger m, final CodeReviewCommit n)
- throws IOException {
- rw.parseBody(n);
-
- final List<FooterLine> footers = n.getFooterLines();
- final StringBuilder msgbuf = new StringBuilder();
- msgbuf.append(n.getFullMessage());
-
- if (msgbuf.length() == 0) {
- // WTF, an empty commit message?
- msgbuf.append("<no commit message provided>");
- }
- if (msgbuf.charAt(msgbuf.length() - 1) != '\n') {
- // Missing a trailing LF? Correct it (perhaps the editor was broken).
- msgbuf.append('\n');
- }
- if (footers.isEmpty()) {
- // Doesn't end in a "Signed-off-by: ..." style line? Add another line
- // break to start a new paragraph for the reviewed-by tag lines.
- //
- msgbuf.append('\n');
- }
-
- if (!contains(footers, CHANGE_ID, n.change.getKey().get())) {
- msgbuf.append(CHANGE_ID.getName());
- msgbuf.append(": ");
- msgbuf.append(n.change.getKey().get());
- msgbuf.append('\n');
- }
-
- final String siteUrl = urlProvider.get();
- if (siteUrl != null) {
- final String url = siteUrl + n.patchsetId.getParentKey().get();
- if (!contains(footers, REVIEWED_ON, url)) {
- msgbuf.append(REVIEWED_ON.getName());
- msgbuf.append(": ");
- msgbuf.append(url);
- msgbuf.append('\n');
- }
- }
-
- PatchSetApproval submitAudit = null;
- try {
- final List<PatchSetApproval> approvalList =
- schema.patchSetApprovals().byPatchSet(n.patchsetId).toList();
- Collections.sort(approvalList, new Comparator<PatchSetApproval>() {
- public int compare(final PatchSetApproval a, final PatchSetApproval b) {
- return a.getGranted().compareTo(b.getGranted());
- }
- });
-
- for (final PatchSetApproval a : approvalList) {
- if (a.getValue() <= 0) {
- // Negative votes aren't counted.
- continue;
- }
-
- if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
- // Submit is treated specially, below (becomes committer)
- //
- if (submitAudit == null
- || a.getGranted().compareTo(submitAudit.getGranted()) > 0) {
- submitAudit = a;
- }
- continue;
- }
-
- final Account acc =
- identifiedUserFactory.create(a.getAccountId()).getAccount();
- final StringBuilder identbuf = new StringBuilder();
- if (acc.getFullName() != null && acc.getFullName().length() > 0) {
- if (identbuf.length() > 0) {
- identbuf.append(' ');
- }
- identbuf.append(acc.getFullName());
- }
- if (acc.getPreferredEmail() != null
- && acc.getPreferredEmail().length() > 0) {
- if (isSignedOffBy(footers, acc.getPreferredEmail())) {
- continue;
- }
- if (identbuf.length() > 0) {
- identbuf.append(' ');
- }
- identbuf.append('<');
- identbuf.append(acc.getPreferredEmail());
- identbuf.append('>');
- }
- if (identbuf.length() == 0) {
- // Nothing reasonable to describe them by? Ignore them.
- continue;
- }
-
- final String tag;
- if (CRVW.equals(a.getCategoryId())) {
- tag = "Reviewed-by";
- } else if (VRIF.equals(a.getCategoryId())) {
- tag = "Tested-by";
- } else {
- final ApprovalType at =
- approvalTypes.getApprovalType(a.getCategoryId());
- if (at == null) {
- // A deprecated/deleted approval type, ignore it.
- continue;
- }
- tag = at.getCategory().getName().replace(' ', '-');
- }
-
- if (!contains(footers, new FooterKey(tag), identbuf.toString())) {
- msgbuf.append(tag);
- msgbuf.append(": ");
- msgbuf.append(identbuf);
- msgbuf.append('\n');
- }
- }
- } catch (OrmException e) {
- log.error("Can't read approval records for " + n.patchsetId, e);
- }
-
- final PersonIdent submitIdent = toPersonIdent(submitAudit);
- final Commit mergeCommit = new Commit(db);
- mergeCommit.setTreeId(m.getResultTreeId());
- mergeCommit.setParentIds(new ObjectId[] {mergeTip});
- mergeCommit.setAuthor(n.getAuthorIdent());
- mergeCommit.setCommitter(submitIdent != null ? submitIdent : myIdent);
- mergeCommit.setMessage(msgbuf.toString());
-
- final ObjectId id = m.getObjectWriter().writeCommit(mergeCommit);
- final CodeReviewCommit newCommit = (CodeReviewCommit) rw.parseCommit(id);
- newCommit.copyFrom(n);
- newCommit.statusCode = CommitMergeStatus.CLEAN_PICK;
- commits.put(newCommit.patchsetId.getParentKey(), newCommit);
-
- mergeTip = newCommit;
- setRefLogIdent(submitAudit);
- }
-
- private boolean contains(List<FooterLine> footers, FooterKey key, String val) {
- for (final FooterLine line : footers) {
- if (line.matches(key) && val.equals(line.getValue())) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isSignedOffBy(List<FooterLine> footers, String email) {
- for (final FooterLine line : footers) {
- if (line.matches(FooterKey.SIGNED_OFF_BY)
- && email.equals(line.getEmailAddress())) {
- return true;
- }
- }
- return false;
- }
-
- private PersonIdent toPersonIdent(final PatchSetApproval audit) {
- if (audit == null) {
- return null;
- }
- return identifiedUserFactory.create(audit.getAccountId()).newPersonIdent(
- audit.getGranted(), myIdent.getTimeZone());
- }
-
- private void updateBranch() throws MergeException {
- if (branchTip == null || branchTip != mergeTip) {
- branchUpdate.setForceUpdate(false);
- branchUpdate.setNewObjectId(mergeTip);
- branchUpdate.setRefLogMessage("merged", true);
- try {
- switch (branchUpdate.update(rw)) {
- case NEW:
- case FAST_FORWARD:
- replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate
- .getName());
- break;
-
- default:
- throw new IOException(branchUpdate.getResult().name());
- }
- } catch (IOException e) {
- throw new MergeException("Cannot update " + branchUpdate.getName(), e);
- }
- }
- }
-
- private void updateChangeStatus() {
- for (final Change c : submitted) {
- final CodeReviewCommit commit = commits.get(c.getId());
- final CommitMergeStatus s = commit != null ? commit.statusCode : null;
- if (s == null) {
- // Shouldn't ever happen, but leave the change alone. We'll pick
- // it up on the next pass.
- //
- continue;
- }
-
- switch (s) {
- case CLEAN_MERGE: {
- final String txt =
- "Change has been successfully merged into the git repository.";
- setMerged(c, message(c, txt));
- break;
- }
-
- case CLEAN_PICK: {
- final String txt =
- "Change has been successfully cherry-picked as " + commit.name()
- + ".";
- setMerged(c, message(c, txt));
- break;
- }
-
- case ALREADY_MERGED:
- setMerged(c, null);
- break;
-
- case PATH_CONFLICT: {
- final String txt =
- "Your change could not be merged due to a path conflict.\n"
- + "\n"
- + "Please merge (or rebase) the change locally and upload the resolution for review.";
- setNew(c, message(c, txt));
- break;
- }
-
- case CRISS_CROSS_MERGE: {
- final String txt =
- "Your change requires a recursive merge to resolve.\n"
- + "\n"
- + "Please merge (or rebase) the change locally and upload the resolution for review.";
- setNew(c, message(c, txt));
- break;
- }
-
- case CANNOT_CHERRY_PICK_ROOT: {
- final String txt =
- "Cannot cherry-pick an initial commit onto an existing branch.\n"
- + "\n"
- + "Please merge the change locally and upload the merge commit for review.";
- setNew(c, message(c, txt));
- break;
- }
-
- case NOT_FAST_FORWARD: {
- final String txt =
- "Project policy requires all submissions to be a fast-forward.\n"
- + "\n"
- + "Please rebase the change locally and upload again for review.";
- setNew(c, message(c, txt));
- break;
- }
-
- case MISSING_DEPENDENCY: {
- dependencyError(commit);
- break;
- }
-
- default:
- setNew(c, message(c, "Unspecified merge failure: " + s.name()));
- break;
- }
- }
- }
-
- private void dependencyError(final CodeReviewCommit commit) {
- final Change c = commit.change;
- if (commit.missing == null) {
- commit.missing = new ArrayList<CodeReviewCommit>();
- }
-
- boolean submitStillPossible = commit.missing.size() > 0;
- for (CodeReviewCommit missingCommit : commit.missing) {
- loadChangeInfo(missingCommit);
-
- if (missingCommit.patchsetId == null) {
- // The commit doesn't have a patch set, so it cannot be
- // submitted to the branch.
- //
- submitStillPossible = false;
- break;
- }
-
- if (!missingCommit.change.currentPatchSetId().equals(
- missingCommit.patchsetId)) {
- // If the missing commit is not the current patch set,
- // the change must be rebased to use the proper parent.
- //
- submitStillPossible = false;
- break;
- }
- }
-
- final long now = System.currentTimeMillis();
- final long waitUntil = c.getLastUpdatedOn().getTime() + DEPENDENCY_DELAY;
- if (submitStillPossible && now < waitUntil) {
- // If we waited a short while we might still be able to get
- // this change submitted. Reschedule an attempt in a bit.
- //
- mergeQueue.recheckAfter(destBranch, waitUntil - now, MILLISECONDS);
-
- } else if (submitStillPossible) {
- // It would be possible to submit the change if the missing
- // dependencies are also submitted. Perhaps the user just
- // forgot to submit those.
- //
- String txt =
- "Change could not be merged because of a missing dependency.";
- if (!isAlreadySent(c, txt)) {
- StringBuilder m = new StringBuilder();
- m.append(txt);
- m.append("\n");
-
- m.append("\n");
-
- m.append("The following changes must also be submitted:\n");
- m.append("\n");
- for (CodeReviewCommit missingCommit : commit.missing) {
- m.append("* ");
- m.append(missingCommit.change.getKey().get());
- m.append("\n");
- }
- txt = m.toString();
- }
-
- sendMergeFail(c, message(c, txt), false);
-
- } else {
- // It is impossible to submit this change as-is. The author
- // needs to rebase it in order to work around the missing
- // dependencies.
- //
- StringBuilder m = new StringBuilder();
- m.append("Change cannot be merged due"
- + " to unsatisfiable dependencies.\n");
- m.append("\n");
- m.append("The following dependency errors were found:\n");
- m.append("\n");
- for (CodeReviewCommit missingCommit : commit.missing) {
- if (missingCommit.patchsetId != null) {
- m.append("* Depends on patch set ");
- m.append(missingCommit.patchsetId.get());
- m.append(" of ");
- m.append(missingCommit.change.getKey().abbreviate());
- m.append(", however the current patch set is ");
- m.append(missingCommit.change.currentPatchSetId().get());
- m.append(".\n");
-
- } else {
- m.append("* Depends on commit ");
- m.append(missingCommit.name());
- m.append(" which has no change associated with it.\n");
- }
- }
- m.append("\n");
- m.append("Please rebase the change and upload a replacement commit.");
-
- setNew(c, message(c, m.toString()));
- }
- }
-
- private void loadChangeInfo(final CodeReviewCommit commit) {
- if (commit.patchsetId == null) {
- try {
- List<PatchSet> matches =
- schema.patchSets().byRevision(new RevId(commit.name())).toList();
- if (matches.size() == 1) {
- final PatchSet ps = matches.get(0);
- commit.patchsetId = ps.getId();
- commit.change = schema.changes().get(ps.getId().getParentKey());
- }
- } catch (OrmException e) {
- }
- }
- }
-
- private boolean isAlreadySent(final Change c, final String prefix) {
- try {
- final List<ChangeMessage> msgList =
- schema.changeMessages().byChange(c.getId()).toList();
- if (msgList.size() > 0) {
- final ChangeMessage last = msgList.get(msgList.size() - 1);
- if (last.getAuthor() == null && last.getMessage().startsWith(prefix)) {
- // The last message was written by us, and it said this
- // same message already. Its unlikely anything has changed
- // that would cause us to need to repeat ourselves.
- //
- return true;
- }
- }
-
- // The last message was not sent by us, or doesn't match the text
- // we are about to send.
- //
- return false;
- } catch (OrmException e) {
- return true;
- }
- }
-
- private ChangeMessage message(final Change c, final String body) {
- final String uuid;
- try {
- uuid = ChangeUtil.messageUUID(schema);
- } catch (OrmException e) {
- return null;
- }
- final ChangeMessage m =
- new ChangeMessage(new ChangeMessage.Key(c.getId(), uuid), null);
- m.setMessage(body);
- return m;
- }
-
- private PatchSetApproval getSubmitter(PatchSet.Id c) {
- if (c == null) {
- return null;
- }
- PatchSetApproval submitter = null;
- try {
- final List<PatchSetApproval> approvals =
- schema.patchSetApprovals().byPatchSet(c).toList();
- for (PatchSetApproval a : approvals) {
- if (a.getValue() > 0
- && ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
- if (submitter == null
- || a.getGranted().compareTo(submitter.getGranted()) > 0) {
- submitter = a;
- }
- }
- }
- } catch (OrmException e) {
- }
- return submitter;
- }
-
- private void setMerged(Change c, ChangeMessage msg) {
- final PatchSet.Id merged = c.currentPatchSetId();
- PatchSetApproval submitter = null;
- for (int attempts = 0; attempts < 10; attempts++) {
- c.setStatus(Change.Status.MERGED);
- ChangeUtil.updated(c);
- try {
- final Transaction txn = schema.beginTransaction();
-
- // Flatten out all existing approvals based upon the current
- // permissions. Once the change is closed the approvals are
- // not updated at presentation view time, so we need to make.
- // sure they are accurate now. This way if permissions get
- // modified in the future, historical records stay accurate.
- //
- final List<PatchSetApproval> approvals =
- schema.patchSetApprovals().byChange(c.getId()).toList();
- final FunctionState fs = functionState.create(c, merged, approvals);
- for (ApprovalType at : approvalTypes.getApprovalTypes()) {
- CategoryFunction.forCategory(at.getCategory()).run(at, fs);
- }
- for (PatchSetApproval a : approvals) {
- if (a.getValue() > 0
- && ApprovalCategory.SUBMIT.equals(a.getCategoryId())
- && a.getPatchSetId().equals(merged)) {
- if (submitter == null
- || a.getGranted().compareTo(submitter.getGranted()) > 0) {
- submitter = a;
- }
- }
- a.cache(c);
- }
- schema.patchSetApprovals().update(approvals, txn);
-
- if (msg != null) {
- if (submitter != null && msg.getAuthor() == null) {
- msg.setAuthor(submitter.getAccountId());
- }
- schema.changeMessages().insert(Collections.singleton(msg), txn);
- }
- schema.changes().update(Collections.singleton(c), txn);
- txn.commit();
- break;
- } catch (OrmException e) {
- final Change.Id id = c.getId();
- try {
- c = schema.changes().get(id);
- if (!merged.equals(c.currentPatchSetId())) {
- // Uncool; the patch set changed after we merged it.
- // Go back to the patch set that was actually merged.
- //
- try {
- c.setCurrentPatchSet(patchSetInfoFactory.get(merged));
- } catch (PatchSetInfoNotAvailableException e1) {
- log.error("Cannot read merged patch set " + merged, e1);
- }
- }
- } catch (OrmException e2) {
- log.error("Cannot set change " + id + " to merged " + merged, e2);
- }
- }
- }
-
- try {
- final MergedSender cm = mergedSenderFactory.create(c);
- if (submitter != null) {
- cm.setFrom(submitter.getAccountId());
- }
- cm.setReviewDb(schema);
- cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
- cm.send();
- } catch (OrmException e) {
- log.error("Cannot send email for submitted patch set " + c.getId(), e);
- } catch (EmailException e) {
- log.error("Cannot send email for submitted patch set " + c.getId(), e);
- }
- }
-
- private void setNew(Change c, ChangeMessage msg) {
- sendMergeFail(c, msg, true);
- }
-
- private void sendMergeFail(Change c, ChangeMessage msg, boolean makeNew) {
- for (int attempts = 0; attempts < 10; attempts++) {
- if (makeNew) {
- c.setStatus(Change.Status.NEW);
- }
- ChangeUtil.updated(c);
- try {
- final Transaction txn = schema.beginTransaction();
- schema.changes().update(Collections.singleton(c), txn);
- if (msg != null) {
- schema.changeMessages().insert(Collections.singleton(msg), txn);
- }
- txn.commit();
- break;
- } catch (OrmException e) {
- try {
- c = schema.changes().get(c.getId());
- if (c.getStatus().isClosed()) {
- // Someone else marked it close while we noticed a failure.
- // That's fine, leave it closed.
- //
- break;
- }
- } catch (OrmException e2) {
- }
- }
- }
-
- try {
- final PatchSetApproval submitter = getSubmitter(c.currentPatchSetId());
- final MergeFailSender cm = mergeFailSenderFactory.create(c);
- if (submitter != null) {
- cm.setFrom(submitter.getAccountId());
- }
- cm.setReviewDb(schema);
- cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()));
- cm.setChangeMessage(msg);
- cm.send();
- } catch (OrmException e) {
- log.error("Cannot send email notifications about merge failure", e);
- } catch (EmailException e) {
- log.error("Cannot send email notifications about merge failure", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/MergeQueue.java b/src/main/java/com/google/gerrit/git/MergeQueue.java
deleted file mode 100644
index aa76e2a9d5..0000000000
--- a/src/main/java/com/google/gerrit/git/MergeQueue.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.Branch;
-
-import java.util.concurrent.TimeUnit;
-
-public interface MergeQueue {
- void merge(Branch.NameKey branch);
-
- void schedule(Branch.NameKey branch);
-
- void recheckAfter(Branch.NameKey branch, long delay, TimeUnit delayUnit);
-}
diff --git a/src/main/java/com/google/gerrit/git/MergeSorter.java b/src/main/java/com/google/gerrit/git/MergeSorter.java
deleted file mode 100644
index c4dedab46c..0000000000
--- a/src/main/java/com/google/gerrit/git/MergeSorter.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevCommitList;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-class MergeSorter {
- private final RevWalk rw;
- private final RevFlag CAN_MERGE;
- private final Set<RevCommit> accepted;
-
- MergeSorter(final RevWalk walk, final Set<RevCommit> alreadyAccepted,
- final RevFlag flagCAN_MERGE) {
- rw = walk;
- CAN_MERGE = flagCAN_MERGE;
- accepted = alreadyAccepted;
- }
-
- Collection<CodeReviewCommit> sort(final Collection<CodeReviewCommit> incoming)
- throws IOException {
- final Set<CodeReviewCommit> heads = new HashSet<CodeReviewCommit>();
- final Set<CodeReviewCommit> sort = new HashSet<CodeReviewCommit>(incoming);
- while (!sort.isEmpty()) {
- final CodeReviewCommit n = removeOne(sort);
-
- rw.resetRetain(CAN_MERGE);
- rw.markStart(n);
- for (RevCommit c : accepted) {
- rw.markUninteresting(c);
- }
-
- RevCommit c;
- final RevCommitList<RevCommit> contents = new RevCommitList<RevCommit>();
- while ((c = rw.next()) != null) {
- if (!c.has(CAN_MERGE)) {
- // We cannot merge n as it would bring something we
- // aren't permitted to merge at this time. Drop n.
- //
- if (n.missing == null) {
- n.statusCode = CommitMergeStatus.MISSING_DEPENDENCY;
- n.missing = new ArrayList<CodeReviewCommit>();
- }
- n.missing.add((CodeReviewCommit) c);
- } else {
- contents.add(c);
- }
- }
-
- if (n.statusCode == CommitMergeStatus.MISSING_DEPENDENCY) {
- continue;
- }
-
- // Anything reachable through us is better merged by just
- // merging us directly. So prune our ancestors out and let
- // us merge instead.
- //
- sort.removeAll(contents);
- heads.removeAll(contents);
- heads.add(n);
- }
- return heads;
- }
-
- private static <T> T removeOne(final Collection<T> c) {
- final Iterator<T> i = c.iterator();
- final T r = i.next();
- i.remove();
- return r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/PatchSetImporter.java b/src/main/java/com/google/gerrit/git/PatchSetImporter.java
deleted file mode 100644
index f9336338b2..0000000000
--- a/src/main/java/com/google/gerrit/git/PatchSetImporter.java
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetAncestor;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.lib.Commit;
-import org.eclipse.jgit.revwalk.RevCommit;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Imports a {@link PatchSet} from a {@link Commit}. */
-public class PatchSetImporter {
- public interface Factory {
- PatchSetImporter create(ReviewDb dstDb, RevCommit srcCommit,
- PatchSet dstPatchSet, boolean isNewPatchSet);
- }
-
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final ReviewDb db;
- private final RevCommit src;
- private final PatchSet dst;
- private final boolean isNew;
- private Transaction txn;
-
- private PatchSetInfo info;
-
- private final Map<Integer, PatchSetAncestor> ancestorExisting =
- new HashMap<Integer, PatchSetAncestor>();
- private final List<PatchSetAncestor> ancestorInsert =
- new ArrayList<PatchSetAncestor>();
- private final List<PatchSetAncestor> ancestorUpdate =
- new ArrayList<PatchSetAncestor>();
-
- @Inject
- PatchSetImporter(final PatchSetInfoFactory psif,
- @Assisted final ReviewDb dstDb, @Assisted final RevCommit srcCommit,
- @Assisted final PatchSet dstPatchSet,
- @Assisted final boolean isNewPatchSet) {
- patchSetInfoFactory = psif;
- db = dstDb;
- src = srcCommit;
- dst = dstPatchSet;
- isNew = isNewPatchSet;
- }
-
- public void setTransaction(final Transaction t) {
- txn = t;
- }
-
- public PatchSetInfo getPatchSetInfo() {
- return info;
- }
-
- public void run() throws OrmException {
- dst.setRevision(toRevId(src));
-
- if (!isNew) {
- for (final PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(
- dst.getId())) {
- ancestorExisting.put(a.getPosition(), a);
- }
- }
-
- info = patchSetInfoFactory.get(src, dst.getId());
- importAncestors();
-
- final boolean auto = txn == null;
- if (auto) {
- txn = db.beginTransaction();
- }
- if (isNew) {
- db.patchSets().insert(Collections.singleton(dst), txn);
- }
- db.patchSetAncestors().insert(ancestorInsert, txn);
- if (!isNew) {
- db.patchSetAncestors().update(ancestorUpdate, txn);
- db.patchSetAncestors().delete(ancestorExisting.values(), txn);
- }
- if (auto) {
- txn.commit();
- txn = null;
- }
- }
-
- private void importAncestors() {
- for (int p = 0; p < src.getParentCount(); p++) {
- PatchSetAncestor a = ancestorExisting.remove(p + 1);
- if (a == null) {
- a = new PatchSetAncestor(new PatchSetAncestor.Id(dst.getId(), p + 1));
- ancestorInsert.add(a);
- } else {
- ancestorUpdate.add(a);
- }
- a.setAncestorRevision(toRevId(src.getParent(p)));
- }
- }
-
- private static RevId toRevId(final RevCommit src) {
- return new RevId(src.getId().name());
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java b/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java
deleted file mode 100644
index ee314ed4f5..0000000000
--- a/src/main/java/com/google/gerrit/git/PushAllProjectsOp.java
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashSet;
-import java.util.concurrent.TimeUnit;
-
-public class PushAllProjectsOp extends DefaultQueueOp {
- public interface Factory {
- PushAllProjectsOp create(String urlMatch);
- }
-
- private static final Logger log =
- LoggerFactory.getLogger(PushAllProjectsOp.class);
-
- private final SchemaFactory<ReviewDb> schema;
- private final ReplicationQueue replication;
- private final Project.NameKey wildProject;
- private final String urlMatch;
-
- @Inject
- public PushAllProjectsOp(final WorkQueue wq,
- final SchemaFactory<ReviewDb> sf, final ReplicationQueue rq,
- @WildProjectName final Project.NameKey wp,
- @Assisted @Nullable final String urlMatch) {
- super(wq);
- this.schema = sf;
- this.replication = rq;
- this.wildProject = wp;
- this.urlMatch = urlMatch;
- }
-
- @Override
- public void start(final int delay, final TimeUnit unit) {
- if (replication.isEnabled()) {
- super.start(delay, unit);
- }
- }
-
- public void run() {
- final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
- try {
- final ReviewDb db = schema.open();
- try {
- for (final Project project : db.projects().all()) {
- if (!project.getNameKey().equals(wildProject)) {
- replication.scheduleFullSync(project.getNameKey(), urlMatch);
- }
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- log.error("Cannot enumerate known projects", e);
- }
- }
-
- @Override
- public String toString() {
- String s = "Replicate All Projects";
- if (urlMatch != null) {
- s = s + " to " + urlMatch;
- }
- return s;
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/PushOp.java b/src/main/java/com/google/gerrit/git/PushOp.java
deleted file mode 100644
index 5555550c3c..0000000000
--- a/src/main/java/com/google/gerrit/git/PushOp.java
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import com.jcraft.jsch.JSchException;
-
-import org.slf4j.Logger;
-import org.eclipse.jgit.errors.NoRemoteRepositoryException;
-import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.errors.TransportException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.NullProgressMonitor;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.FetchConnection;
-import org.eclipse.jgit.transport.PushResult;
-import org.eclipse.jgit.transport.RefSpec;
-import org.eclipse.jgit.transport.RemoteConfig;
-import org.eclipse.jgit.transport.RemoteRefUpdate;
-import org.eclipse.jgit.transport.Transport;
-import org.eclipse.jgit.transport.URIish;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A push to remote operation started by {@link ReplicationQueue}.
- * <p>
- * Instance members are protected by the lock within PushQueue. Callers must
- * take that lock to ensure they are working with a current view of the object.
- */
-class PushOp implements Runnable {
- interface Factory {
- PushOp create(String d, URIish u);
- }
-
- private static final Logger log = PushReplication.log;
- static final String MIRROR_ALL = "..all..";
-
- private final GitRepositoryManager repoManager;
- private final PushReplication.ReplicationConfig pool;
- private final RemoteConfig config;
-
- private final Set<String> delta = new HashSet<String>();
- private final String projectName;
- private final URIish uri;
- private boolean mirror;
-
- private Repository db;
-
- @Inject
- PushOp(final GitRepositoryManager grm, final PushReplication.ReplicationConfig p,
- final RemoteConfig c, @Assisted final String d, @Assisted final URIish u) {
- repoManager = grm;
- pool = p;
- config = c;
- projectName = d;
- uri = u;
- }
-
- URIish getURI() {
- return uri;
- }
-
- void addRef(final String ref) {
- if (MIRROR_ALL.equals(ref)) {
- delta.clear();
- mirror = true;
- } else if (!mirror) {
- delta.add(ref);
- }
- }
-
- public void run() {
- try {
- // Lock the queue, and remove ourselves, so we can't be modified once
- // we start replication (instead a new instance, with the same URI, is
- // created and scheduled for a future point in time.)
- //
- pool.notifyStarting(this);
- db = repoManager.openRepository(projectName);
- runImpl();
- } catch (RepositoryNotFoundException e) {
- log.error("Cannot replicate " + projectName + "; " + e.getMessage());
-
- } catch (NoRemoteRepositoryException e) {
- log.error("Cannot replicate to " + uri + "; repository not found");
-
- } catch (NotSupportedException e) {
- log.error("Cannot replicate to " + uri, e);
-
- } catch (TransportException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof JSchException
- && cause.getMessage().startsWith("UnknownHostKey:")) {
- log.error("Cannot replicate to " + uri + ": " + cause.getMessage());
- } else {
- log.error("Cannot replicate to " + uri, e);
- }
-
- } catch (IOException e) {
- log.error("Cannot replicate to " + uri, e);
-
- } catch (RuntimeException e) {
- log.error("Unexpected error during replication to " + uri, e);
-
- } catch (Error e) {
- log.error("Unexpected error during replication to " + uri, e);
-
- } finally {
- if (db != null) {
- db.close();
- }
- }
- }
-
- @Override
- public String toString() {
- return (mirror ? "mirror " : "push ") + uri;
- }
-
- private void runImpl() throws IOException {
- final Transport tn = Transport.open(db, uri);
- final PushResult res;
- try {
- res = pushVia(tn);
- } finally {
- try {
- tn.close();
- } catch (Throwable e2) {
- log.warn("Unexpected error while closing " + uri, e2);
- }
- }
-
- for (final RemoteRefUpdate u : res.getRemoteUpdates()) {
- switch (u.getStatus()) {
- case OK:
- case UP_TO_DATE:
- case NON_EXISTING:
- break;
-
- case NOT_ATTEMPTED:
- case AWAITING_REPORT:
- case REJECTED_NODELETE:
- case REJECTED_NONFASTFORWARD:
- case REJECTED_REMOTE_CHANGED:
- log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
- + ": status " + u.getStatus().name());
- break;
-
- case REJECTED_OTHER_REASON:
- log.error("Failed replicate of " + u.getRemoteName() + " to " + uri
- + ", reason: " + u.getMessage());
- break;
- }
- }
- }
-
- private PushResult pushVia(final Transport tn) throws IOException,
- NotSupportedException, TransportException {
- tn.applyConfig(config);
-
- final List<RemoteRefUpdate> todo = generateUpdates(tn);
- if (todo.isEmpty()) {
- // If we have no commands selected, we have nothing to do.
- // Calling JGit at this point would just redo the work we
- // already did, and come up with the same answer. Instead
- // send back an empty result.
- //
- return new PushResult();
- }
-
- return tn.push(NullProgressMonitor.INSTANCE, todo);
- }
-
- private List<RemoteRefUpdate> generateUpdates(final Transport tn)
- throws IOException {
- final List<RemoteRefUpdate> cmds = new ArrayList<RemoteRefUpdate>();
- final Map<String, Ref> local = db.getAllRefs();
-
- if (mirror) {
- final Map<String, Ref> remote = listRemote(tn);
-
- for (final Ref src : local.values()) {
- final RefSpec spec = matchSrc(src.getOrigName());
- if (spec != null) {
- final Ref dst = remote.get(spec.getDestination());
- if (dst == null || !src.getObjectId().equals(dst.getObjectId())) {
- // Doesn't exist yet, or isn't the same value, request to push.
- //
- send(cmds, spec);
- }
- }
- }
-
- for (final Ref ref : remote.values()) {
- if (!isHEAD(ref)) {
- final RefSpec spec = matchDst(ref.getName());
- if (spec != null && !local.containsKey(spec.getSource())) {
- // No longer on local side, request removal.
- //
- delete(cmds, spec);
- }
- }
- }
-
- } else {
- for (final String src : delta) {
- final RefSpec spec = matchSrc(src);
- if (spec != null) {
- // If the ref still exists locally, send it, otherwise delete it.
- //
- if (local.containsKey(src)) {
- send(cmds, spec);
- } else {
- delete(cmds, spec);
- }
- }
- }
- }
-
- return cmds;
- }
-
- private Map<String, Ref> listRemote(final Transport tn)
- throws NotSupportedException, TransportException {
- final FetchConnection fc = tn.openFetch();
- try {
- return fc.getRefsMap();
- } finally {
- fc.close();
- }
- }
-
- private RefSpec matchSrc(final String ref) {
- for (final RefSpec s : config.getPushRefSpecs()) {
- if (s.matchSource(ref)) {
- return s.expandFromSource(ref);
- }
- }
- return null;
- }
-
- private RefSpec matchDst(final String ref) {
- for (final RefSpec s : config.getPushRefSpecs()) {
- if (s.matchDestination(ref)) {
- return s.expandFromDestination(ref);
- }
- }
- return null;
- }
-
- private void send(final List<RemoteRefUpdate> cmds, final RefSpec spec)
- throws IOException {
- final String src = spec.getSource();
- final String dst = spec.getDestination();
- final boolean force = spec.isForceUpdate();
- cmds.add(new RemoteRefUpdate(db, src, dst, force, null, null));
- }
-
- private void delete(final List<RemoteRefUpdate> cmds, final RefSpec spec)
- throws IOException {
- final String dst = spec.getDestination();
- final boolean force = spec.isForceUpdate();
- cmds.add(new RemoteRefUpdate(db, null, dst, force, null, null));
- }
-
- private static boolean isHEAD(final Ref ref) {
- return Constants.HEAD.equals(ref.getOrigName());
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/PushReplication.java b/src/main/java/com/google/gerrit/git/PushReplication.java
deleted file mode 100644
index aea8b7ccbe..0000000000
--- a/src/main/java/com/google/gerrit/git/PushReplication.java
+++ /dev/null
@@ -1,433 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.ReplicationUser;
-import com.google.gerrit.server.config.SitePath;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Singleton;
-import com.google.inject.assistedinject.FactoryProvider;
-
-import com.jcraft.jsch.Channel;
-import com.jcraft.jsch.ChannelExec;
-import com.jcraft.jsch.JSchException;
-import com.jcraft.jsch.Session;
-
-import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.FileBasedConfig;
-import org.eclipse.jgit.transport.OpenSshConfig;
-import org.eclipse.jgit.transport.RefSpec;
-import org.eclipse.jgit.transport.RemoteConfig;
-import org.eclipse.jgit.transport.SshConfigSessionFactory;
-import org.eclipse.jgit.transport.SshSessionFactory;
-import org.eclipse.jgit.transport.URIish;
-import org.eclipse.jgit.util.QuotedString;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-/** Manages automatic replication to remote repositories. */
-@Singleton
-public class PushReplication implements ReplicationQueue {
- static final Logger log = LoggerFactory.getLogger(PushReplication.class);
-
- static {
- // Install our own factory which always runs in batch mode, as we
- // have no UI available for interactive prompting.
- //
- SshSessionFactory.setInstance(new SshConfigSessionFactory() {
- @Override
- protected void configure(OpenSshConfig.Host hc, Session session) {
- // Default configuration is batch mode.
- }
- });
- }
-
- private final Injector injector;
- private final WorkQueue workQueue;
- private final List<ReplicationConfig> configs;
- private final SchemaFactory<ReviewDb> database;
- private final ReplicationUser.Factory replicationUserFactory;
-
- @Inject
- PushReplication(final Injector i, final WorkQueue wq,
- @SitePath final File sitePath, final ReplicationUser.Factory ruf,
- final SchemaFactory<ReviewDb> db) throws ConfigInvalidException,
- IOException {
- injector = i;
- workQueue = wq;
- database = db;
- replicationUserFactory = ruf;
- configs = allConfigs(sitePath);
- }
-
- @Override
- public boolean isEnabled() {
- return configs.size() > 0;
- }
-
- @Override
- public void scheduleFullSync(final Project.NameKey project,
- final String urlMatch) {
- for (final ReplicationConfig cfg : configs) {
- for (final URIish uri : cfg.getURIs(project, urlMatch)) {
- cfg.schedule(project, PushOp.MIRROR_ALL, uri);
- }
- }
- }
-
- @Override
- public void scheduleUpdate(final Project.NameKey project, final String ref) {
- for (final ReplicationConfig cfg : configs) {
- if (cfg.wouldPushRef(ref)) {
- for (final URIish uri : cfg.getURIs(project, null)) {
- cfg.schedule(project, ref, uri);
- }
- }
- }
- }
-
- private static String replace(final String pat, final String key,
- final String val) {
- final int n = pat.indexOf("${" + key + "}");
- return pat.substring(0, n) + val + pat.substring(n + 3 + key.length());
- }
-
- private List<ReplicationConfig> allConfigs(final File path)
- throws ConfigInvalidException, IOException {
- final File cfgFile = new File(path, "replication.config");
- final FileBasedConfig cfg = new FileBasedConfig(cfgFile);
-
- if (!cfg.getFile().exists()) {
- log.warn("No " + cfg.getFile() + "; not replicating");
- return Collections.emptyList();
- }
-
- try {
- cfg.load();
- } catch (ConfigInvalidException e) {
- throw new ConfigInvalidException("Config file " + cfg.getFile()
- + " is invalid: " + e.getMessage(), e);
- } catch (IOException e) {
- throw new IOException("Cannot read " + cfgFile + ": " + e.getMessage(), e);
- }
-
- final List<ReplicationConfig> r = new ArrayList<ReplicationConfig>();
- for (final RemoteConfig c : allRemotes(cfg)) {
- if (c.getURIs().isEmpty()) {
- continue;
- }
-
- for (final URIish u : c.getURIs()) {
- if (u.getPath() == null || !u.getPath().contains("${name}")) {
- throw new ConfigInvalidException("remote." + c.getName() + ".url"
- + " \"" + u + "\" lacks ${name} placeholder in " + cfg.getFile());
- }
- }
-
- if (c.getPushRefSpecs().isEmpty()) {
- RefSpec spec = new RefSpec();
- spec = spec.setSourceDestination("refs/*", "refs/*");
- spec = spec.setForceUpdate(true);
- c.addPushRefSpec(spec);
- }
-
- r.add(new ReplicationConfig(injector, workQueue, c, cfg, database,
- replicationUserFactory));
- }
- return Collections.unmodifiableList(r);
- }
-
- private List<RemoteConfig> allRemotes(final FileBasedConfig cfg)
- throws ConfigInvalidException {
- List<String> names = new ArrayList<String>(cfg.getSubsections("remote"));
- Collections.sort(names);
-
- final List<RemoteConfig> result = new ArrayList<RemoteConfig>(names.size());
- for (final String name : names) {
- try {
- result.add(new RemoteConfig(cfg, name));
- } catch (URISyntaxException e) {
- throw new ConfigInvalidException("remote " + name
- + " has invalid URL in " + cfg.getFile());
- }
- }
- return result;
- }
-
- public void replicateNewProject(Project.NameKey projectName, String head) {
- if (!isEnabled()) {
- return;
- }
-
- Iterator<ReplicationConfig> configIter = configs.iterator();
-
- while (configIter.hasNext()) {
- ReplicationConfig rp = configIter.next();
- List<URIish> uriList = rp.getURIs(projectName, "*");
-
- Iterator<URIish> uriIter = uriList.iterator();
-
- while (uriIter.hasNext()) {
- replicateProject(uriIter.next(), head);
- }
- }
- }
-
- private void replicateProject(final URIish replicateURI, final String head) {
- SshSessionFactory sshFactory = SshSessionFactory.getInstance();
- Session sshSession;
- String projectPath = QuotedString.BOURNE.quote(replicateURI.getPath());
-
- if (!usingSSH(replicateURI)) {
- log.warn("Cannot create new project on remote site since the connection "
- + "method is not SSH: " + replicateURI.toString());
- return;
- }
-
- OutputStream errStream = createErrStream();
- String cmd =
- "mkdir -p " + projectPath + "&& cd " + projectPath
- + "&& git init --bare" + "&& git symbolic-ref HEAD "
- + QuotedString.BOURNE.quote(head);
-
- try {
- sshSession =
- sshFactory.getSession(replicateURI.getUser(), replicateURI.getPass(),
- replicateURI.getHost(), replicateURI.getPort());
- sshSession.connect();
-
- Channel channel = sshSession.openChannel("exec");
- ((ChannelExec) channel).setCommand(cmd);
-
- channel.setInputStream(null);
-
- ((ChannelExec) channel).setErrStream(errStream);
-
- channel.connect();
-
- while (!channel.isClosed()) {
- try {
- final int delay = 50;
- Thread.sleep(delay);
- } catch (InterruptedException e) {
- }
- }
- channel.disconnect();
- sshSession.disconnect();
- } catch (JSchException e) {
- log.error("Communication error when trying to replicate to: "
- + replicateURI.toString() + "\n" + "Error reported: "
- + e.getMessage() + "\n" + "Error in communication: "
- + errStream.toString());
- }
- }
-
- private OutputStream createErrStream() {
- return new OutputStream() {
- private StringBuilder all = new StringBuilder();
- private StringBuilder sb = new StringBuilder();
-
- public String toString() {
- String r = all.toString();
- while (r.endsWith("\n"))
- r = r.substring(0, r.length() - 1);
- return r;
- }
-
- @Override
- public void write(final int b) throws IOException {
- if (b == '\r') {
- return;
- }
-
- sb.append((char) b);
-
- if (b == '\n') {
- all.append(sb);
- sb.setLength(0);
- }
- }
- };
- }
-
- private boolean usingSSH(final URIish uri) {
- final String scheme = uri.getScheme();
- if (!uri.isRemote()) return false;
- if (scheme != null && scheme.toLowerCase().contains("ssh")) return true;
- if (scheme == null && uri.getHost() != null && uri.getPath() != null)
- return true;
- return false;
- }
-
- static class ReplicationConfig {
- private final RemoteConfig remote;
- private final int delay;
- private final WorkQueue.Executor pool;
- private final Map<URIish, PushOp> pending = new HashMap<URIish, PushOp>();
- private final PushOp.Factory opFactory;
- private final ProjectControl.Factory projectControlFactory;
- private final boolean authEnabled;
-
- ReplicationConfig(final Injector injector, final WorkQueue workQueue,
- final RemoteConfig rc, final Config cfg, SchemaFactory<ReviewDb> db,
- final ReplicationUser.Factory replicationUserFactory) {
-
- remote = rc;
- delay = Math.max(0, getInt(rc, cfg, "replicationdelay", 15));
-
- final int poolSize = Math.max(0, getInt(rc, cfg, "threads", 1));
- final String poolName = "ReplicateTo-" + rc.getName();
- pool = workQueue.createQueue(poolSize, poolName);
-
- String[] authGroupNames =
- cfg.getStringList("remote", rc.getName(), "authGroup");
- authEnabled = authGroupNames.length > 0;
- Set<AccountGroup.Id> authGroups = groupsFor(db, authGroupNames);
-
- final ReplicationUser remoteUser =
- replicationUserFactory.create(authGroups);
-
- projectControlFactory =
- injector.createChildInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(CurrentUser.class).toInstance(remoteUser);
- }
- }).getInstance(ProjectControl.Factory.class);
-
- opFactory = injector.createChildInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(PushReplication.ReplicationConfig.class).toInstance(
- ReplicationConfig.this);
- bind(RemoteConfig.class).toInstance(remote);
- bind(PushOp.Factory.class).toProvider(
- FactoryProvider.newFactory(PushOp.Factory.class, PushOp.class));
- }
- }).getInstance(PushOp.Factory.class);
- }
-
- private static Set<AccountGroup.Id> groupsFor(
- SchemaFactory<ReviewDb> dbfactory, String[] groupNames) {
- final Set<AccountGroup.Id> result = new HashSet<AccountGroup.Id>();
- try {
- final ReviewDb db = dbfactory.open();
- try {
- for (String name : groupNames) {
- AccountGroup group =
- db.accountGroups().get(new AccountGroup.NameKey(name));
- if (group == null) {
- log.warn("Group \"" + name + "\" not in database,"
- + " removing from authGroup");
- } else {
- result.add(group.getId());
- }
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- log.error("Database error: " + e);
- }
- return result;
- }
-
- private int getInt(final RemoteConfig rc, final Config cfg,
- final String name, final int defValue) {
- return cfg.getInt("remote", rc.getName(), name, defValue);
- }
-
- void schedule(final Project.NameKey project, final String ref,
- final URIish uri) {
- try {
- if (authEnabled
- && !projectControlFactory.controlFor(project).isVisible()) {
- return;
- }
- } catch (NoSuchProjectException e1) {
- log.error("Internal error: project " + project
- + " not found during replication");
- return;
- }
- synchronized (pending) {
- PushOp e = pending.get(uri);
- if (e == null) {
- e = opFactory.create(project.get(), uri);
- pool.schedule(e, delay, TimeUnit.SECONDS);
- pending.put(uri, e);
- }
- e.addRef(ref);
- }
- }
-
- void notifyStarting(final PushOp op) {
- synchronized (pending) {
- pending.remove(op.getURI());
- }
- }
-
- boolean wouldPushRef(final String ref) {
- for (final RefSpec s : remote.getPushRefSpecs()) {
- if (s.matchSource(ref)) {
- return true;
- }
- }
- return false;
- }
-
- List<URIish> getURIs(final Project.NameKey project, final String urlMatch) {
- final List<URIish> r = new ArrayList<URIish>(remote.getURIs().size());
- for (URIish uri : remote.getURIs()) {
- if (matches(uri, urlMatch)) {
- uri = uri.setPath(replace(uri.getPath(), "name", project.get()));
- r.add(uri);
- }
- }
- return r;
- }
-
- private boolean matches(URIish uri, final String urlMatch) {
- if (urlMatch == null || urlMatch.equals("") || urlMatch.equals("*")) {
- return true;
- }
- return uri.toString().contains(urlMatch);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/ReloadSubmitQueueOp.java b/src/main/java/com/google/gerrit/git/ReloadSubmitQueueOp.java
deleted file mode 100644
index a56d2ed640..0000000000
--- a/src/main/java/com/google/gerrit/git/ReloadSubmitQueueOp.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashSet;
-
-public class ReloadSubmitQueueOp extends DefaultQueueOp {
- public interface Factory {
- ReloadSubmitQueueOp create();
- }
-
- private static final Logger log =
- LoggerFactory.getLogger(ReloadSubmitQueueOp.class);
-
- private final SchemaFactory<ReviewDb> schema;
- private final MergeQueue mergeQueue;
-
- @Inject
- ReloadSubmitQueueOp(final WorkQueue wq, final SchemaFactory<ReviewDb> sf,
- final MergeQueue mq) {
- super(wq);
- schema = sf;
- mergeQueue = mq;
- }
-
- public void run() {
- final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
- try {
- final ReviewDb c = schema.open();
- try {
- for (final Change change : c.changes().allSubmitted()) {
- pending.add(change.getDest());
- }
- } finally {
- c.close();
- }
- } catch (OrmException e) {
- log.error("Cannot reload MergeQueue", e);
- }
-
- for (final Branch.NameKey branch : pending) {
- mergeQueue.schedule(branch);
- }
- }
-
- @Override
- public String toString() {
- return "Reload Submit Queue";
- }
-}
diff --git a/src/main/java/com/google/gerrit/git/ReplicationQueue.java b/src/main/java/com/google/gerrit/git/ReplicationQueue.java
deleted file mode 100644
index 2d71301533..0000000000
--- a/src/main/java/com/google/gerrit/git/ReplicationQueue.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.gerrit.client.reviewdb.Project;
-
-/** Manages replication to other nodes. */
-public interface ReplicationQueue {
- /** Is replication to one or more other destinations configured? */
- boolean isEnabled();
-
- /**
- * Schedule a full replication for a single project.
- * <p>
- * All remote URLs are checked to verify the are current with regards to the
- * local project state. If not, they are updated by pushing new refs, updating
- * existing ones which don't match, and deleting stale refs which have been
- * removed from the local repository.
- *
- * @param project identity of the project to replicate.
- * @param urlMatch substring that must appear in a URI to support replication.
- */
- void scheduleFullSync(Project.NameKey project, String urlMatch);
-
- /**
- * Schedule update of a single ref.
- * <p>
- * This method automatically tries to batch together multiple requests in the
- * same project, to take advantage of Git's native ability to update multiple
- * refs during a single push operation.
- *
- * @param project identity of the project to replicate.
- * @param ref unique name of the ref; must start with {@code refs/}.
- */
- void scheduleUpdate(Project.NameKey project, String ref);
-
- /**
- * Create new empty project at the remote sites.
- * <p>
- * When a new project has been created locally call this method to make sure
- * that the project will be created at the remote sites as well.
- *
- * @param project of the project to be created.
- * @param head name HEAD should point at (must be {@code refs/heads/...}).
- */
- void replicateNewProject(Project.NameKey project, String head);
-}
diff --git a/src/main/java/com/google/gerrit/git/WorkQueue.java b/src/main/java/com/google/gerrit/git/WorkQueue.java
deleted file mode 100644
index 27e8c3e08e..0000000000
--- a/src/main/java/com/google/gerrit/git/WorkQueue.java
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.git;
-
-import com.google.inject.Singleton;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Delayed;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.RunnableScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/** Delayed execution of tasks using a background thread pool. */
-@Singleton
-public class WorkQueue {
- private Executor defaultQueue;
- private final CopyOnWriteArrayList<Executor> queues =
- new CopyOnWriteArrayList<Executor>();
-
- /** Get the default work queue, for miscellaneous tasks. */
- public synchronized Executor getDefaultQueue() {
- if (defaultQueue == null) {
- defaultQueue = createQueue(1, "WorkQueue");
- }
- return defaultQueue;
- }
-
- /** Create a new executor queue with one thread. */
- public Executor createQueue(final int poolsize, final String prefix) {
- final Executor r = new Executor(poolsize, prefix);
- r.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
- r.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
- queues.add(r);
- return r;
- }
-
- /** Get all of the tasks currently scheduled in any work queue. */
- public List<Task<?>> getTasks() {
- final List<Task<?>> r = new ArrayList<Task<?>>();
- for (final Executor e : queues) {
- e.addAllTo(r);
- }
- return r;
- }
-
- /** Shutdown all queues, aborting any pending tasks that haven't started. */
- public void shutdown() {
- for (final Executor p : queues) {
- p.shutdown();
- boolean isTerminated;
- do {
- try {
- isTerminated = p.awaitTermination(10, TimeUnit.SECONDS);
- } catch (InterruptedException ie) {
- isTerminated = false;
- }
- } while (!isTerminated);
- }
- queues.clear();
- }
-
- /** An isolated queue. */
- public static class Executor extends ScheduledThreadPoolExecutor {
- private final Set<Task<?>> active = new HashSet<Task<?>>();
-
- Executor(final int corePoolSize, final String prefix) {
- super(corePoolSize, new ThreadFactory() {
- private final ThreadFactory parent = Executors.defaultThreadFactory();
- private final AtomicInteger tid = new AtomicInteger(1);
-
- @Override
- public Thread newThread(final Runnable task) {
- final Thread t = parent.newThread(task);
- t.setName(prefix + "-thread-" + tid.getAndIncrement());
- return t;
- }
- });
- }
-
- @Override
- protected <V> RunnableScheduledFuture<V> decorateTask(
- final Runnable runnable, final RunnableScheduledFuture<V> task) {
- return new Task<V>(runnable, super.decorateTask(runnable, task));
- }
-
- @Override
- protected <V> RunnableScheduledFuture<V> decorateTask(
- final Callable<V> callable, final RunnableScheduledFuture<V> task) {
- throw new UnsupportedOperationException("Callable not implemented");
- }
-
- @Override
- protected void beforeExecute(Thread t, Runnable r) {
- super.beforeExecute(t, r);
- synchronized (active) {
- active.add((Task<?>) r);
- }
- }
-
- @Override
- protected void afterExecute(Runnable r, Throwable t) {
- super.afterExecute(r, t);
- synchronized (active) {
- active.remove(r);
- }
- }
-
- void addAllTo(final List<Task<?>> list) {
- synchronized (active) {
- list.addAll(active);
- }
- for (final Runnable task : getQueue()) { // iterator is thread safe
- list.add((Task<?>) task);
- }
- }
- }
-
- /** A wrapper around a scheduled Runnable, as maintained in the queue. */
- public static class Task<V> implements RunnableScheduledFuture<V> {
- /**
- * Summarized status of a single task.
- * <p>
- * Tasks have the following state flow:
- * <ol>
- * <li>{@link #SLEEPING}: if scheduled with a non-zero delay.</li>
- * <li>{@link #READY}: waiting for an available worker thread.</li>
- * <li>{@link #RUNNING}: actively executing on a worker thread.</li>
- * <li>{@link #DONE}: finished executing, if not periodic.</li>
- * </ol>
- */
- public static enum State {
- // Ordered like this so ordinal matches the order we would
- // prefer to see tasks sorted in: done before running,
- // running before ready, ready before sleeping.
- //
- DONE, CANCELLED, RUNNING, READY, SLEEPING, OTHER;
- }
-
- private final Runnable runnable;
- private final RunnableScheduledFuture<V> task;
- private volatile boolean running;
-
- Task(Runnable runnable, RunnableScheduledFuture<V> task) {
- this.runnable = runnable;
- this.task = task;
- }
-
- /** Get the Runnable this task executes. */
- public Runnable getRunnable() {
- return runnable;
- }
-
- public State getState() {
- if (isDone() && !isPeriodic()) {
- return State.DONE;
- } else if (isRunning()) {
- return State.RUNNING;
- } else if (isCancelled()) {
- return State.CANCELLED;
- }
-
- final long delay = getDelay(TimeUnit.MILLISECONDS);
- if (delay <= 0) {
- return State.READY;
- } else if (0 < delay) {
- return State.SLEEPING;
- }
-
- return State.OTHER;
- }
-
- public boolean cancel(boolean mayInterruptIfRunning) {
- return task.cancel(mayInterruptIfRunning);
- }
-
- public int compareTo(Delayed o) {
- return task.compareTo(o);
- }
-
- public V get() throws InterruptedException, ExecutionException {
- return task.get();
- }
-
- public V get(long timeout, TimeUnit unit) throws InterruptedException,
- ExecutionException, TimeoutException {
- return task.get(timeout, unit);
- }
-
- public long getDelay(TimeUnit unit) {
- return task.getDelay(unit);
- }
-
- public boolean isCancelled() {
- return task.isCancelled();
- }
-
- public boolean isRunning() {
- return running;
- }
-
- public boolean isDone() {
- return task.isDone();
- }
-
- public boolean isPeriodic() {
- return task.isPeriodic();
- }
-
- public void run() {
- try {
- running = true;
- task.run();
- } finally {
- running = false;
- }
- }
-
- @Override
- public String toString() {
- return runnable.toString();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/pgm/AbstractProgram.java b/src/main/java/com/google/gerrit/pgm/AbstractProgram.java
deleted file mode 100644
index e358a1054b..0000000000
--- a/src/main/java/com/google/gerrit/pgm/AbstractProgram.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.Option;
-
-import java.io.StringWriter;
-import java.util.Collections;
-
-/** Base class for command line invocations of Gerrit Code Review. */
-public abstract class AbstractProgram {
- private final Object sleepLock = new Object();
- private boolean running = true;
-
- @Option(name = "--help", usage = "display this help text", aliases = {"-h"})
- private boolean help;
-
- private String getName() {
- String n = getClass().getName();
- int dot = n.lastIndexOf('.');
- if (0 < dot) {
- n = n.substring(dot + 1);
- }
- return n.toLowerCase();
- }
-
- public final int main(final String[] argv) throws Exception {
- final Injector empty = emptyInjector();
- final CmdLineParser clp = new CmdLineParser(empty, this);
- try {
- clp.parseArgument(argv);
- } catch (CmdLineException err) {
- if (!help) {
- System.err.println("fatal: " + err.getMessage());
- return 1;
- }
- }
-
- if (help) {
- final StringWriter msg = new StringWriter();
- msg.write(getName());
- clp.printSingleLineUsage(msg, null);
- msg.write('\n');
-
- msg.write('\n');
- clp.printUsage(msg, null);
- msg.write('\n');
- System.err.println(msg.toString());
- return 1;
- }
-
- return run();
- }
-
- private static Injector emptyInjector() {
- return Guice.createInjector(Collections.<Module> emptyList());
- }
-
- /** Method that never returns, e.g. to keep a daemon running. */
- protected int never() {
- synchronized (sleepLock) {
- while (running) {
- try {
- sleepLock.wait(60 * 60 * 1000L);
- } catch (InterruptedException e) {
- continue;
- }
- }
- return 0;
- }
- }
-
- /**
- * Run this program's logic, returning the command exit status.
- * <p>
- * When this method completes, the JVM is terminated. To keep the JVM running,
- * use {@code return never()}.
- */
- public abstract int run() throws Exception;
-}
diff --git a/src/main/java/com/google/gerrit/pgm/CmdLineParser.java b/src/main/java/com/google/gerrit/pgm/CmdLineParser.java
deleted file mode 100644
index 70d14a24ae..0000000000
--- a/src/main/java/com/google/gerrit/pgm/CmdLineParser.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- *
- * (Taken from JGit org.eclipse.jgit.pgm.opt.CmdLineParser.)
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Git Development Community nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.google.gerrit.pgm;
-
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.assistedinject.Assisted;
-
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.IllegalAnnotationError;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
-
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.ResourceBundle;
-
-/**
- * Extended command line parser which handles --foo=value arguments.
- * <p>
- * The args4j package does not natively handle --foo=value and instead prefers
- * to see --foo value on the command line. Many users are used to the GNU style
- * --foo=value long option, so we convert from the GNU style format to the
- * args4j style format prior to invoking args4j for parsing.
- */
-public class CmdLineParser {
- public interface Factory {
- CmdLineParser create(Object bean);
- }
-
- private final Injector injector;
- private final MyParser parser;
-
- /**
- * Creates a new command line owner that parses arguments/options and set them
- * into the given object.
- *
- * @param bean instance of a class annotated by
- * {@link org.kohsuke.args4j.Option} and
- * {@link org.kohsuke.args4j.Argument}. this object will receive
- * values.
- *
- * @throws IllegalAnnotationError if the option bean class is using args4j
- * annotations incorrectly.
- */
- @Inject
- public CmdLineParser(final Injector injector, @Assisted final Object bean)
- throws IllegalAnnotationError {
- this.injector = injector;
- this.parser = new MyParser(bean);
- }
-
- public void addArgument(Setter<?> setter, Argument a) {
- parser.addArgument(setter, a);
- }
-
- public void addOption(Setter<?> setter, Option o) {
- parser.addOption(setter, o);
- }
-
- public void printSingleLineUsage(Writer w, ResourceBundle rb) {
- parser.printSingleLineUsage(w, rb);
- }
-
- public void printUsage(Writer out, ResourceBundle rb) {
- parser.printUsage(out, rb);
- }
-
- public void parseArgument(final String... args) throws CmdLineException {
- final ArrayList<String> tmp = new ArrayList<String>(args.length);
- for (int argi = 0; argi < args.length; argi++) {
- final String str = args[argi];
- if (str.equals("--")) {
- while (argi < args.length)
- tmp.add(args[argi++]);
- break;
- }
-
- if (str.startsWith("--")) {
- final int eq = str.indexOf('=');
- if (eq > 0) {
- tmp.add(str.substring(0, eq));
- tmp.add(str.substring(eq + 1));
- continue;
- }
- }
-
- tmp.add(str);
- }
-
- parser.parseArgument(tmp.toArray(new String[tmp.size()]));
- }
-
- private class MyParser extends org.kohsuke.args4j.CmdLineParser {
- MyParser(final Object bean) {
- super(bean);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- protected OptionHandler createOptionHandler(final OptionDef option,
- final Setter setter) {
- if (isHandlerSpecified(option) || isEnum(setter) || isPrimitive(setter)) {
- return super.createOptionHandler(option, setter);
- }
-
- final Key<OptionHandlerFactory<?>> key =
- OptionHandlerUtil.keyFor(setter.getType());
- Injector i = injector;
- while (i != null) {
- if (i.getBindings().containsKey(key)) {
- return i.getInstance(key).create(this, option, setter);
- }
- i = i.getParent();
- }
-
- return super.createOptionHandler(option, setter);
- }
-
- private boolean isHandlerSpecified(final OptionDef option) {
- return option.handler() != OptionHandler.class;
- }
-
- @SuppressWarnings("unchecked")
- private boolean isEnum(final Setter setter) {
- return Enum.class.isAssignableFrom(setter.getType());
- }
-
- @SuppressWarnings("unchecked")
- private boolean isPrimitive(final Setter setter) {
- return setter.getType().isPrimitive();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/pgm/CreateSchema.java b/src/main/java/com/google/gerrit/pgm/CreateSchema.java
deleted file mode 100644
index 1f31722892..0000000000
--- a/src/main/java/com/google/gerrit/pgm/CreateSchema.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-import static com.google.inject.Stage.PRODUCTION;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.SchemaVersion;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gerrit.server.config.DatabaseModule;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-
-/**
- * Creates the Gerrit 2 database schema.
- */
-public class CreateSchema extends AbstractProgram {
- @Inject
- private SystemConfig systemConfig;
-
- @Inject
- private SchemaFactory<ReviewDb> schema;
-
- @Override
- public int run() throws Exception {
- final Injector injector =
- Guice.createInjector(PRODUCTION, new DatabaseModule());
- injector.injectMembers(this);
-
- final SchemaVersion sv;
- final ReviewDb db = schema.open();
- try {
- sv = db.schemaVersion().get(new SchemaVersion.Key());
- } finally {
- db.close();
- }
- if (sv == null) {
- System.err.println("Schema failed to initialize");
- return 1;
- }
-
- System.out.println("Gerrit Code Review initialized.");
- System.out.println("Current settings:");
- System.out.println(" schema version = " + sv.versionNbr);
- System.out.println(" admin group = " + systemConfig.adminGroupId);
- System.out.println(" site_path = " + systemConfig.sitePath);
- System.out.println();
- return 0;
- }
-}
diff --git a/src/main/java/com/google/gerrit/pgm/Daemon.java b/src/main/java/com/google/gerrit/pgm/Daemon.java
deleted file mode 100644
index 473c3a1f06..0000000000
--- a/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.config.GerritGlobalModule;
-import com.google.gerrit.server.ssh.SshDaemon;
-import com.google.gerrit.server.ssh.SshModule;
-import com.google.gerrit.server.ssh.commands.MasterCommandModule;
-import com.google.gerrit.server.ssh.commands.SlaveCommandModule;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-
-import org.kohsuke.args4j.Option;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Run only the SSH daemon portions of Gerrit. */
-public class Daemon extends AbstractProgram {
-
- @Option(name = "--slave", usage = "support fetch only")
- boolean slave;
-
- @Override
- public int run() throws Exception {
- Injector sysInjector = GerritGlobalModule.createInjector();
- Injector sshInjector = createSshInjector(sysInjector);
- sysInjector.getInstance(CachePool.class).start();
- sshInjector.getInstance(SshDaemon.class).start();
- return never();
- }
-
- private Injector createSshInjector(final Injector sysInjector) {
- final List<Module> modules = new ArrayList<Module>();
- modules.add(new SshModule());
- if (slave) {
- modules.add(new SlaveCommandModule());
- } else {
- modules.add(new MasterCommandModule());
- }
- return sysInjector.createChildInjector(modules);
- }
-}
diff --git a/src/main/java/com/google/gerrit/pgm/OptionHandlerFactory.java b/src/main/java/com/google/gerrit/pgm/OptionHandlerFactory.java
deleted file mode 100644
index 37e12ce013..0000000000
--- a/src/main/java/com/google/gerrit/pgm/OptionHandlerFactory.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
-
-/** Creates an args4j OptionHandler through a Guice Injector. */
-public interface OptionHandlerFactory<T> {
- @SuppressWarnings("unchecked")
- OptionHandler create(org.kohsuke.args4j.CmdLineParser cmdLineParser,
- OptionDef optionDef, Setter setter);
-}
diff --git a/src/main/java/com/google/gerrit/pgm/OptionHandlerUtil.java b/src/main/java/com/google/gerrit/pgm/OptionHandlerUtil.java
deleted file mode 100644
index 898455218a..0000000000
--- a/src/main/java/com/google/gerrit/pgm/OptionHandlerUtil.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import com.google.inject.internal.MoreTypes.ParameterizedTypeImpl;
-
-import java.lang.reflect.Type;
-
-/** Utilities to support creating OptionHandler instances. */
-public class OptionHandlerUtil {
- /** Generate a key for an {@link OptionHandlerFactory} in Guice. */
- @SuppressWarnings("unchecked")
- public static <T> Key<OptionHandlerFactory<T>> keyFor(final Class<T> valueType) {
- final Type factoryType =
- new ParameterizedTypeImpl(null, OptionHandlerFactory.class, valueType);
-
- return (Key<OptionHandlerFactory<T>>) Key.get(TypeLiteral.get(factoryType));
- }
-
- private OptionHandlerUtil() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/pgm/Version.java b/src/main/java/com/google/gerrit/pgm/Version.java
deleted file mode 100644
index 6c48680c42..0000000000
--- a/src/main/java/com/google/gerrit/pgm/Version.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.pgm;
-
-import com.google.gerrit.client.GerritVersion;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-
-/** Display the version of Gerrit. */
-public class Version extends AbstractProgram {
- private static String version;
-
- public static synchronized String getVersion() {
- if (version == null) {
- final Properties p = new Properties();
- final InputStream in =
- GerritVersion.class.getResourceAsStream(GerritVersion.class
- .getSimpleName()
- + ".properties");
- if (in == null) {
- return null;
- }
- try {
- try {
- p.load(in);
- } finally {
- in.close();
- }
- version = p.getProperty("version");
- } catch (IOException e) {
- return null;
- }
- }
- return version;
- }
-
- @Override
- public int run() throws Exception {
- final String v = getVersion();
- if (v == null) {
- System.err.println("fatal: version unavailable");
- return 1;
- }
- System.out.println("gerrit version " + v);
- return 0;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/AnonymousUser.java b/src/main/java/com/google/gerrit/server/AnonymousUser.java
deleted file mode 100644
index 73472f0ba3..0000000000
--- a/src/main/java/com/google/gerrit/server/AnonymousUser.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.util.Collections;
-import java.util.Set;
-
-/** An anonymous user who has not yet authenticated. */
-@Singleton
-public class AnonymousUser extends CurrentUser {
- @Inject
- AnonymousUser(final AuthConfig auth) {
- super(AccessPath.UNKNOWN, auth);
- }
-
- @Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
- return authConfig.getAnonymousGroups();
- }
-
- @Override
- public Set<Change.Id> getStarredChanges() {
- return Collections.emptySet();
- }
-
- @Override
- public String toString() {
- return "ANONYMOUS";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/BaseServiceImplementation.java b/src/main/java/com/google/gerrit/server/BaseServiceImplementation.java
deleted file mode 100644
index 8fcd9e81ad..0000000000
--- a/src/main/java/com/google/gerrit/server/BaseServiceImplementation.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.CorruptEntityException;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.account.NoSuchGroupException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Provider;
-
-/** Support for services which require a {@link ReviewDb} instance. */
-public class BaseServiceImplementation {
- private final Provider<ReviewDb> schema;
- private final Provider<? extends CurrentUser> currentUser;
-
- protected BaseServiceImplementation(final Provider<ReviewDb> schema,
- final Provider<? extends CurrentUser> currentUser) {
- this.schema = schema;
- this.currentUser = currentUser;
- }
-
- @Deprecated
- protected Account.Id getAccountId() {
- CurrentUser u = currentUser.get();
- if (u instanceof IdentifiedUser) {
- return ((IdentifiedUser) u).getAccountId();
- }
- return null;
- }
-
- /**
- * Executes <code>action.run</code> with an active ReviewDb connection.
- * <p>
- * A database handle is automatically opened and closed around the action's
- * {@link Action#run(ReviewDb)} method. OrmExceptions are caught and passed
- * into the onFailure method of the callback.
- *
- * @param <T> type of result the callback expects.
- * @param callback the callback that will receive the result.
- * @param action the action logic to perform.
- */
- protected <T> void run(final AsyncCallback<T> callback, final Action<T> action) {
- try {
- final T r = action.run(schema.get());
- if (r != null) {
- callback.onSuccess(r);
- }
- } catch (NoSuchProjectException e) {
- callback.onFailure(new NoSuchEntityException());
- } catch (NoSuchGroupException e) {
- callback.onFailure(new NoSuchEntityException());
-
- } catch (OrmException e) {
- if (e.getCause() instanceof Failure) {
- callback.onFailure(e.getCause().getCause());
-
- } else if (e.getCause() instanceof CorruptEntityException) {
- callback.onFailure(e.getCause());
-
- } else if (e.getCause() instanceof NoSuchEntityException) {
- callback.onFailure(e.getCause());
-
- } else {
- callback.onFailure(e);
- }
- } catch (Failure e) {
- if (e.getCause() instanceof NoSuchProjectException
- || e.getCause() instanceof NoSuchChangeException
- || e.getCause() instanceof NoSuchGroupException) {
- callback.onFailure(new NoSuchEntityException());
-
- } else {
- callback.onFailure(e.getCause());
- }
- }
- }
-
- /** Exception whose cause is passed into onFailure. */
- @Deprecated
- public static class Failure extends Exception {
- private static final long serialVersionUID = 1L;
-
- public Failure(final Throwable why) {
- super(why);
- }
- }
-
- /** Arbitrary action to run with a database connection. */
- public static interface Action<T> {
- /**
- * Perform this action, returning the onSuccess value.
- *
- * @param db an open database handle to be used by this connection.
- * @return he value to pass to {@link AsyncCallback#onSuccess(Object)}.
- * @throws OrmException any schema based action failed.
- * @throws Failure cause is given to
- * {@link AsyncCallback#onFailure(Throwable)}.
- * @throws NoSuchProjectException
- * @throws NoSuchGroupException
- */
- T run(ReviewDb db) throws OrmException, Failure, NoSuchProjectException,
- NoSuchGroupException;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ChangeUtil.java b/src/main/java/com/google/gerrit/server/ChangeUtil.java
deleted file mode 100644
index 8fe9bb9f31..0000000000
--- a/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-
-import org.eclipse.jgit.util.Base64;
-import org.eclipse.jgit.util.NB;
-
-public class ChangeUtil {
- private static int uuidPrefix;
- private static int uuidSeq;
-
- /**
- * Generate a new unique identifier for change message entities.
- *
- * @param db the database connection, used to increment the change message
- * allocation sequence.
- * @return the new unique identifier.
- * @throws OrmException the database couldn't be incremented.
- */
- public static String messageUUID(final ReviewDb db) throws OrmException {
- final byte[] raw = new byte[8];
- fill(raw, db);
- return Base64.encodeBytes(raw);
- }
-
- private static synchronized void fill(byte[] raw, ReviewDb db)
- throws OrmException {
- if (uuidSeq == 0) {
- uuidPrefix = db.nextChangeMessageId();
- uuidSeq = Integer.MAX_VALUE;
- }
- NB.encodeInt32(raw, 0, uuidPrefix);
- NB.encodeInt32(raw, 4, uuidSeq--);
- }
-
- public static void updated(final Change c) {
- c.resetLastUpdatedOn();
- computeSortKey(c);
- }
-
- public static void computeSortKey(final Change c) {
- // The encoding uses minutes since Wed Oct 1 00:00:00 2008 UTC.
- // We overrun approximately 4,085 years later, so ~6093.
- //
- final long lastUpdatedOn =
- (c.getLastUpdatedOn().getTime() / 1000L) - 1222819200L;
- final StringBuilder r = new StringBuilder(16);
- r.setLength(16);
- formatHexInt(r, 0, (int) (lastUpdatedOn / 60));
- formatHexInt(r, 8, c.getId().get());
- c.setSortKey(r.toString());
- }
-
- private static final char[] hexchar =
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
- 'a', 'b', 'c', 'd', 'e', 'f'};
-
- private static void formatHexInt(final StringBuilder dst, final int p, int w) {
- int o = p + 7;
- while (o >= p && w != 0) {
- dst.setCharAt(o--, hexchar[w & 0xf]);
- w >>>= 4;
- }
- while (o >= p) {
- dst.setCharAt(o--, '0');
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/CurrentUser.java b/src/main/java/com/google/gerrit/server/CurrentUser.java
deleted file mode 100644
index adbbcaca46..0000000000
--- a/src/main/java/com/google/gerrit/server/CurrentUser.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.inject.servlet.RequestScoped;
-
-import java.util.Set;
-
-/**
- * Information about the currently logged in user.
- * <p>
- * This is a {@link RequestScoped} property managed by Guice.
- *
- * @see AnonymousUser
- * @see IdentifiedUser
- */
-public abstract class CurrentUser {
- private final AccessPath accessPath;
- protected final AuthConfig authConfig;
-
- protected CurrentUser(final AccessPath accessPath, final AuthConfig authConfig) {
- this.accessPath = accessPath;
- this.authConfig = authConfig;
- }
-
- /** How this user is accessing the Gerrit Code Review application. */
- public final AccessPath getAccessPath() {
- return accessPath;
- }
-
- /**
- * Get the set of groups the user is currently a member of.
- * <p>
- * The returned set may be a subset of the user's actual groups; if the user's
- * account is currently deemed to be untrusted then the effective group set is
- * only the anonymous and registered user groups. To enable additional groups
- * (and gain their granted permissions) the user must update their account to
- * use only trusted authentication providers.
- *
- * @return active groups for this user.
- */
- public abstract Set<AccountGroup.Id> getEffectiveGroups();
-
- /** Set of changes starred by this user. */
- public abstract Set<Change.Id> getStarredChanges();
-
- @Deprecated
- public final boolean isAdministrator() {
- return getEffectiveGroups().contains(authConfig.getAdministratorsGroup());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/src/main/java/com/google/gerrit/server/IdentifiedUser.java
deleted file mode 100644
index 52d93cfff1..0000000000
--- a/src/main/java/com/google/gerrit/server/IdentifiedUser.java
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.StarredChange;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.lib.PersonIdent;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.TimeZone;
-
-/** An authenticated user. */
-public class IdentifiedUser extends CurrentUser {
- /** Create an IdentifiedUser, ignoring any per-request state. */
- @Singleton
- public static class GenericFactory {
- private final AuthConfig authConfig;
- private final AccountCache accountCache;
- private final Realm realm;
-
- @Inject
- GenericFactory(final AuthConfig authConfig,
- final AccountCache accountCache, final Realm realm) {
- this.authConfig = authConfig;
- this.accountCache = accountCache;
- this.realm = realm;
- }
-
- public IdentifiedUser create(final Account.Id id) {
- return new IdentifiedUser(AccessPath.UNKNOWN, authConfig, accountCache,
- realm, null, null, id);
- }
- }
-
- /**
- * Create an IdentifiedUser, relying on current request state.
- * <p>
- * Can only be used from within a module that has defined request scoped
- * {@code @RemotePeer SocketAddress} and {@code ReviewDb} providers.
- */
- @Singleton
- public static class RequestFactory {
- private final AuthConfig authConfig;
- private final AccountCache accountCache;
- private final Realm realm;
- private final Provider<SocketAddress> remotePeerProvider;
- private final Provider<ReviewDb> dbProvider;
-
- @Inject
- RequestFactory(final AuthConfig authConfig,
- final AccountCache accountCache, final Realm realm,
- final @RemotePeer Provider<SocketAddress> remotePeerProvider,
- final Provider<ReviewDb> dbProvider) {
- this.authConfig = authConfig;
- this.accountCache = accountCache;
- this.realm = realm;
- this.remotePeerProvider = remotePeerProvider;
- this.dbProvider = dbProvider;
- }
-
- public IdentifiedUser create(final AccessPath accessPath,
- final Account.Id id) {
- return new IdentifiedUser(accessPath, authConfig, accountCache, realm,
- remotePeerProvider, dbProvider, id);
- }
- }
-
- private static final Logger log =
- LoggerFactory.getLogger(IdentifiedUser.class);
-
- private final Realm realm;
- private final AccountCache accountCache;
-
- @Nullable
- private final Provider<SocketAddress> remotePeerProvider;
-
- @Nullable
- private final Provider<ReviewDb> dbProvider;
-
- private final Account.Id accountId;
-
- private AccountState state;
- private Set<String> emailAddresses;
- private Set<AccountGroup.Id> effectiveGroups;
- private Set<Change.Id> starredChanges;
-
- private IdentifiedUser(final AccessPath accessPath,
- final AuthConfig authConfig, final AccountCache accountCache,
- final Realm realm,
- @Nullable final Provider<SocketAddress> remotePeerProvider,
- @Nullable final Provider<ReviewDb> dbProvider, final Account.Id id) {
- super(accessPath, authConfig);
- this.realm = realm;
- this.accountCache = accountCache;
- this.remotePeerProvider = remotePeerProvider;
- this.dbProvider = dbProvider;
- this.accountId = id;
- }
-
- private AccountState state() {
- if (state == null) {
- state = accountCache.get(getAccountId());
- }
- return state;
- }
-
- /** The account identity for the user. */
- public Account.Id getAccountId() {
- return accountId;
- }
-
- public Account getAccount() {
- return state().getAccount();
- }
-
- public Set<String> getEmailAddresses() {
- if (emailAddresses == null) {
- emailAddresses = state().getEmailAddresses();
- }
- return emailAddresses;
- }
-
- @Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
- if (effectiveGroups == null) {
- if (authConfig.isIdentityTrustable(state().getExternalIds())) {
- effectiveGroups = realm.groups(state());
-
- } else {
- effectiveGroups = authConfig.getRegisteredGroups();
- }
- }
- return effectiveGroups;
- }
-
- @Override
- public Set<Change.Id> getStarredChanges() {
- if (starredChanges == null) {
- if (dbProvider == null) {
- throw new OutOfScopeException("Not in request scoped user");
- }
- final Set<Change.Id> h = new HashSet<Change.Id>();
- try {
- for (final StarredChange sc : dbProvider.get().starredChanges()
- .byAccount(getAccountId())) {
- h.add(sc.getChangeId());
- }
- } catch (ProvisionException e) {
- log.warn("Cannot query starred by user changes", e);
- } catch (OrmException e) {
- log.warn("Cannot query starred by user changes", e);
- }
- starredChanges = Collections.unmodifiableSet(h);
- }
- return starredChanges;
- }
-
- public PersonIdent newPersonIdent() {
- return newPersonIdent(new Date(), TimeZone.getDefault());
- }
-
- public PersonIdent newPersonIdent(final Date when, final TimeZone tz) {
- final Account ua = getAccount();
- String name = ua.getFullName();
- if (name == null) {
- name = ua.getPreferredEmail();
- }
- if (name == null) {
- name = "Anonymous Coward";
- }
-
- final String userId = "account-" + ua.getId().toString();
- final String user;
- if (ua.getSshUserName() != null) {
- user = ua.getSshUserName() + "|" + userId;
- } else {
- user = userId;
- }
-
- String host = null;
- final SocketAddress remotePeer =
- remotePeerProvider != null ? remotePeerProvider.get() : null;
- if (remotePeer instanceof InetSocketAddress) {
- final InetSocketAddress sa = (InetSocketAddress) remotePeer;
- final InetAddress in = sa.getAddress();
- if (in != null) {
- host = in.getCanonicalHostName();
- } else {
- host = sa.getHostName();
- }
- }
- if (host == null) {
- host = "unknown";
- }
-
- return new PersonIdent(name, user + "@" + host, when, tz);
- }
-
- @Override
- public String toString() {
- return "IdentifiedUser[account " + getAccountId() + "]";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ReplicationUser.java b/src/main/java/com/google/gerrit/server/ReplicationUser.java
deleted file mode 100644
index 0885af8e39..0000000000
--- a/src/main/java/com/google/gerrit/server/ReplicationUser.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-public class ReplicationUser extends CurrentUser {
- public interface Factory {
- ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups);
- }
-
- private Set<AccountGroup.Id> effectiveGroups;
-
- @Inject
- protected ReplicationUser(AuthConfig authConfig,
- @Assisted Set<AccountGroup.Id> authGroups) {
- super(AccessPath.REPLICATION, authConfig);
- effectiveGroups = new HashSet<AccountGroup.Id>(authGroups);
-
- if (effectiveGroups.isEmpty()) {
- effectiveGroups.addAll(authConfig.getRegisteredGroups());
- }
-
- effectiveGroups = Collections.unmodifiableSet(effectiveGroups);
- }
-
- @Override
- public Set<AccountGroup.Id> getEffectiveGroups() {
- return Collections.unmodifiableSet(effectiveGroups);
- }
-
- @Override
- public Set<Change.Id> getStarredChanges() {
- return Collections.emptySet();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java b/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
deleted file mode 100644
index 89e022d9fa..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-import java.util.Set;
-
-/** Translates an email address to a set of matching accounts. */
-public interface AccountByEmailCache {
- public Set<Account.Id> get(String email);
-
- public void evict(String email);
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java b/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
deleted file mode 100644
index cfabdf8b0d..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/** Translates an email address to a set of matching accounts. */
-@Singleton
-public class AccountByEmailCacheImpl implements AccountByEmailCache {
- private static final String CACHE_NAME = "accounts_byemail";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<String, Set<Account.Id>>> type =
- new TypeLiteral<Cache<String, Set<Account.Id>>>() {};
- core(type, CACHE_NAME);
- bind(AccountByEmailCacheImpl.class);
- bind(AccountByEmailCache.class).to(AccountByEmailCacheImpl.class);
- }
- };
- }
-
- private final SchemaFactory<ReviewDb> schema;
- private final SelfPopulatingCache<String, Set<Account.Id>> self;
-
- @Inject
- AccountByEmailCacheImpl(final SchemaFactory<ReviewDb> schema,
- @Named(CACHE_NAME) final Cache<String, Set<Account.Id>> rawCache) {
- this.schema = schema;
- this.self = new SelfPopulatingCache<String, Set<Account.Id>>(rawCache) {
- @Override
- protected Set<Account.Id> createEntry(final String key) throws Exception {
- return lookup(key);
- }
-
- @Override
- protected Set<Account.Id> missing(final String key) {
- return Collections.emptySet();
- }
- };
- }
-
- private Set<Account.Id> lookup(final String email) throws OrmException {
- final ReviewDb db = schema.open();
- try {
- final HashSet<Account.Id> r = new HashSet<Account.Id>();
- for (Account a : db.accounts().byPreferredEmail(email)) {
- r.add(a.getId());
- }
- for (AccountExternalId a : db.accountExternalIds().byEmailAddress(email)) {
- r.add(a.getAccountId());
- }
- return pack(r);
- } finally {
- db.close();
- }
- }
-
- public Set<Account.Id> get(final String email) {
- return self.get(email);
- }
-
- public void evict(final String email) {
- self.remove(email);
- }
-
- private static Set<Account.Id> pack(final Set<Account.Id> c) {
- switch (c.size()) {
- case 0:
- return Collections.emptySet();
- case 1:
- return one(c);
- default:
- return Collections.unmodifiableSet(new HashSet<Account.Id>(c));
- }
- }
-
- private static <T> Set<T> one(final Set<T> c) {
- return Collections.singleton(c.iterator().next());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountCache.java b/src/main/java/com/google/gerrit/server/account/AccountCache.java
deleted file mode 100644
index 4c8b2eb5a9..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountCache.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Caches important (but small) account state to avoid database hits. */
-public interface AccountCache {
- public AccountState get(Account.Id accountId);
-
- public void evict(Account.Id accountId);
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
deleted file mode 100644
index 3a347c8463..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/** Caches important (but small) account state to avoid database hits. */
-@Singleton
-public class AccountCacheImpl implements AccountCache {
- private static final String CACHE_NAME = "accounts";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<Account.Id, AccountState>> type =
- new TypeLiteral<Cache<Account.Id, AccountState>>() {};
- core(type, CACHE_NAME);
- bind(AccountCacheImpl.class);
- bind(AccountCache.class).to(AccountCacheImpl.class);
- }
- };
- }
-
- private final SchemaFactory<ReviewDb> schema;
- private final GroupCache groupCache;
- private final SelfPopulatingCache<Account.Id, AccountState> self;
-
- private final Set<AccountGroup.Id> registered;
- private final Set<AccountGroup.Id> anonymous;
-
- @Inject
- AccountCacheImpl(final SchemaFactory<ReviewDb> sf, final AuthConfig auth,
- final GroupCache groupCache,
- @Named(CACHE_NAME) final Cache<Account.Id, AccountState> rawCache) {
- schema = sf;
- registered = auth.getRegisteredGroups();
- anonymous = auth.getAnonymousGroups();
- this.groupCache = groupCache;
-
- self = new SelfPopulatingCache<Account.Id, AccountState>(rawCache) {
- @Override
- protected AccountState createEntry(Account.Id key) throws Exception {
- return lookup(key);
- }
-
- @Override
- protected AccountState missing(final Account.Id key) {
- return missingAccount(key);
- }
- };
- }
-
- private AccountState lookup(final Account.Id who) throws OrmException {
- final ReviewDb db = schema.open();
- try {
- final Account account = db.accounts().get(who);
- if (account == null) {
- // Account no longer exists? They are anonymous.
- //
- return missingAccount(who);
- }
-
- final Collection<AccountExternalId> externalIds =
- Collections.unmodifiableCollection(db.accountExternalIds().byAccount(
- who).toList());
-
- Set<AccountGroup.Id> internalGroups = new HashSet<AccountGroup.Id>();
- for (AccountGroupMember g : db.accountGroupMembers().byAccount(who)) {
- final AccountGroup.Id groupId = g.getAccountGroupId();
- final AccountGroup group = groupCache.get(groupId);
- if (group != null && group.getType() == AccountGroup.Type.INTERNAL) {
- internalGroups.add(groupId);
- }
- }
-
- if (internalGroups.isEmpty()) {
- internalGroups = registered;
- } else {
- internalGroups.addAll(registered);
- internalGroups = Collections.unmodifiableSet(internalGroups);
- }
-
- return new AccountState(account, internalGroups, externalIds);
- } finally {
- db.close();
- }
- }
-
- private AccountState missingAccount(final Account.Id accountId) {
- final Account account = new Account(accountId);
- final Collection<AccountExternalId> ids = Collections.emptySet();
- return new AccountState(account, anonymous, ids);
- }
-
- public AccountState get(final Account.Id accountId) {
- return self.get(accountId);
- }
-
- public void evict(final Account.Id accountId) {
- self.remove(accountId);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java b/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java
deleted file mode 100644
index af7681b01f..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountInfoCacheFactory.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Efficiently builds an {@link AccountInfoCache}. */
-public class AccountInfoCacheFactory {
- public interface Factory {
- AccountInfoCacheFactory create();
- }
-
- private final AccountCache accountCache;
- private final Map<Account.Id, Account> out;
-
- @Inject
- AccountInfoCacheFactory(final AccountCache accountCache) {
- this.accountCache = accountCache;
- this.out = new HashMap<Account.Id, Account>();
- }
-
- /**
- * Indicate an account will be needed later on.
- *
- * @param id identity that will be needed in the future; may be null.
- */
- public void want(final Account.Id id) {
- if (id != null && !out.containsKey(id)) {
- out.put(id, accountCache.get(id).getAccount());
- }
- }
-
- /** Indicate one or more accounts will be needed later on. */
- public void want(final Iterable<Account.Id> ids) {
- for (final Account.Id id : ids) {
- want(id);
- }
- }
-
- public Account get(final Account.Id id) {
- want(id);
- return out.get(id);
- }
-
- /**
- * Create an AccountInfoCache with the currently loaded Account entities.
- * */
- public AccountInfoCache create() {
- final List<AccountInfo> r = new ArrayList<AccountInfo>(out.size());
- for (final Account a : out.values()) {
- r.add(new AccountInfo(a));
- }
- return new AccountInfoCache(r);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountManager.java b/src/main/java/com/google/gerrit/server/account/AccountManager.java
deleted file mode 100644
index 0224403096..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.auth.openid.OpenIdUtil;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tracks authentication related details for user accounts. */
-@Singleton
-public class AccountManager {
- private final SchemaFactory<ReviewDb> schema;
- private final AccountCache byIdCache;
- private final AccountByEmailCache byEmailCache;
- private final AuthConfig authConfig;
- private final Realm realm;
-
- @Inject
- AccountManager(final SchemaFactory<ReviewDb> schema,
- final AccountCache byIdCache, final AccountByEmailCache byEmailCache,
- final AuthConfig authConfig, final Realm accountMapper) {
- this.schema = schema;
- this.byIdCache = byIdCache;
- this.byEmailCache = byEmailCache;
- this.authConfig = authConfig;
- this.realm = accountMapper;
- }
-
- /**
- * @return user identified by this external identity string, or null.
- */
- public Account.Id lookup(final String externalId) throws AccountException {
- try {
- final ReviewDb db = schema.open();
- try {
- final AccountExternalId ext =
- db.accountExternalIds().get(new AccountExternalId.Key(externalId));
- return ext != null ? ext.getAccountId() : null;
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new AccountException("Cannot lookup account " + externalId, e);
- }
- }
-
- /**
- * Authenticate the user, potentially creating a new account if they are new.
- *
- * @param who identity of the user, with any details we received about them.
- * @return the result of authenticating the user.
- * @throws AccountException the account does not exist, and cannot be created,
- * or exists, but cannot be located.
- */
- public AuthResult authenticate(AuthRequest who) throws AccountException {
- who = realm.authenticate(who);
- try {
- final ReviewDb db = schema.open();
- try {
- final AccountExternalId id =
- db.accountExternalIds().get(
- new AccountExternalId.Key(who.getExternalId()));
- if (id == null) {
- // New account, automatically create and return.
- //
- return create(db, who);
-
- } else {
- // Account exists, return the identity to the caller.
- //
- update(db, who, id);
- return new AuthResult(id.getAccountId(), false);
- }
-
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new AccountException("Authentication error", e);
- }
- }
-
- private void update(final ReviewDb db, final AuthRequest who,
- final AccountExternalId extId) throws OrmException, AccountException {
- final Transaction txn = db.beginTransaction();
- final Account account = db.accounts().get(extId.getAccountId());
- boolean updateAccount = false;
- if (account == null) {
- throw new AccountException("Account has been deleted");
- }
-
- // If the email address was modified by the authentication provider,
- // update our records to match the changed email.
- //
- final String newEmail = who.getEmailAddress();
- final String oldEmail = extId.getEmailAddress();
- if (newEmail != null && !newEmail.equals(oldEmail)) {
- if (oldEmail != null && oldEmail.equals(account.getPreferredEmail())) {
- updateAccount = true;
- account.setPreferredEmail(newEmail);
- }
-
- extId.setEmailAddress(newEmail);
- }
-
- if (!realm.allowsEdit(Account.FieldName.FULL_NAME)
- && !eq(account.getFullName(), who.getDisplayName())) {
- updateAccount = true;
- account.setFullName(who.getDisplayName());
- }
- if (!realm.allowsEdit(Account.FieldName.SSH_USER_NAME)
- && !eq(account.getSshUserName(), who.getSshUserName())) {
- updateAccount = true;
- account.setSshUserName(who.getSshUserName());
- }
-
- extId.setLastUsedOn();
- db.accountExternalIds().update(Collections.singleton(extId), txn);
- if (updateAccount) {
- db.accounts().update(Collections.singleton(account), txn);
- }
- txn.commit();
-
- if (newEmail != null && !newEmail.equals(oldEmail)) {
- byEmailCache.evict(oldEmail);
- byEmailCache.evict(newEmail);
- }
- if (updateAccount) {
- byIdCache.evict(account.getId());
- }
- }
-
- private static boolean eq(final String a, final String b) {
- return (a == null && b == null) || (a != null && a.equals(b));
- }
-
- private AuthResult create(final ReviewDb db, final AuthRequest who)
- throws OrmException, AccountException {
- if (authConfig.isAllowGoogleAccountUpgrade()
- && who.isScheme(OpenIdUtil.URL_GOOGLE + "?")
- && who.getEmailAddress() != null) {
- final List<AccountExternalId> openId = new ArrayList<AccountExternalId>();
- final List<AccountExternalId> v1 = new ArrayList<AccountExternalId>();
-
- for (final AccountExternalId extId : db.accountExternalIds()
- .byEmailAddress(who.getEmailAddress())) {
- if (extId.isScheme(OpenIdUtil.URL_GOOGLE + "?")) {
- openId.add(extId);
- } else if (extId.isScheme(AccountExternalId.LEGACY_GAE)) {
- v1.add(extId);
- }
- }
-
- if (!openId.isEmpty()) {
- // The user has already registered with an OpenID from Google, but
- // Google may have changed the user's OpenID identity if this server
- // name has changed. Insert a new identity for the user.
- //
- final Account.Id accountId = openId.get(0).getAccountId();
-
- if (openId.size() > 1) {
- // Validate all matching identities are actually the same user.
- //
- for (final AccountExternalId extId : openId) {
- if (!accountId.equals(extId.getAccountId())) {
- throw new AccountException("Multiple user accounts for "
- + who.getEmailAddress() + " using Google Accounts provider");
- }
- }
- }
-
- final AccountExternalId newId = createId(accountId, who);
- newId.setEmailAddress(who.getEmailAddress());
- newId.setLastUsedOn();
-
- if (openId.size() == 1) {
- final AccountExternalId oldId = openId.get(0);
- final Transaction txn = db.beginTransaction();
- db.accountExternalIds().delete(Collections.singleton(oldId), txn);
- db.accountExternalIds().insert(Collections.singleton(newId), txn);
- txn.commit();
- } else {
- db.accountExternalIds().insert(Collections.singleton(newId));
- }
- return new AuthResult(accountId, false);
-
- } else if (v1.size() == 1) {
- // Exactly one user was imported from Gerrit 1.x with this email
- // address. Upgrade their account by deleting the legacy import
- // identity and creating a new identity matching the token we have.
- //
- final AccountExternalId oldId = v1.get(0);
- final AccountExternalId newId = createId(oldId.getAccountId(), who);
- newId.setEmailAddress(who.getEmailAddress());
- newId.setLastUsedOn();
- final Transaction txn = db.beginTransaction();
- db.accountExternalIds().delete(Collections.singleton(oldId), txn);
- db.accountExternalIds().insert(Collections.singleton(newId), txn);
- txn.commit();
- return new AuthResult(newId.getAccountId(), false);
-
- } else if (v1.size() > 1) {
- throw new AccountException("Multiple Gerrit 1.x accounts found");
- }
- }
-
- final Account.Id newId = new Account.Id(db.nextAccountId());
- final Account account = new Account(newId);
- final AccountExternalId extId = createId(newId, who);
-
- extId.setLastUsedOn();
- extId.setEmailAddress(who.getEmailAddress());
- account.setFullName(who.getDisplayName());
- account.setPreferredEmail(extId.getEmailAddress());
-
- if (who.getSshUserName() != null
- && db.accounts().bySshUserName(who.getSshUserName()) == null) {
- // Only set if the name hasn't been used yet, but was given to us.
- //
- account.setSshUserName(who.getSshUserName());
- }
-
- final Transaction txn = db.beginTransaction();
- db.accounts().insert(Collections.singleton(account), txn);
- db.accountExternalIds().insert(Collections.singleton(extId), txn);
- txn.commit();
-
- byEmailCache.evict(account.getPreferredEmail());
- realm.onCreateAccount(who, account);
- return new AuthResult(newId, true);
- }
-
- private static AccountExternalId createId(final Account.Id newId,
- final AuthRequest who) {
- final String ext = who.getExternalId();
- return new AccountExternalId(newId, new AccountExternalId.Key(ext));
- }
-
- /**
- * Link another authentication identity to an existing account.
- *
- * @param to account to link the identity onto.
- * @param who the additional identity.
- * @throws AccountException the identity belongs to a different account, or it
- * cannot be linked at this time.
- */
- public void link(final Account.Id to, final AuthRequest who)
- throws AccountException {
- try {
- final ReviewDb db = schema.open();
- try {
- AccountExternalId extId =
- db.accountExternalIds().get(
- new AccountExternalId.Key(who.getExternalId()));
- if (extId != null) {
- if (!extId.getAccountId().equals(to)) {
- throw new AccountException("Identity in use by another account");
- }
- update(db, who, extId);
- } else {
- extId = createId(to, who);
- extId.setEmailAddress(who.getEmailAddress());
- extId.setLastUsedOn();
- db.accountExternalIds().insert(Collections.singleton(extId));
- if (who.getEmailAddress() != null) {
- byEmailCache.evict(who.getEmailAddress());
- byIdCache.evict(to);
- }
- }
-
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new AccountException("Cannot link identity", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountResolver.java b/src/main/java/com/google/gerrit/server/account/AccountResolver.java
deleted file mode 100644
index ff36faebb7..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountResolver.java
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.ResultSet;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.List;
-import java.util.Set;
-
-public class AccountResolver {
- private final Realm realm;
- private final AccountByEmailCache byEmail;
- private final AccountCache byId;
- private final Provider<ReviewDb> schema;
-
- @Inject
- AccountResolver(final Realm realm, final AccountByEmailCache byEmail,
- final AccountCache byId, final Provider<ReviewDb> schema) {
- this.realm = realm;
- this.byEmail = byEmail;
- this.byId = byId;
- this.schema = schema;
- }
-
- /**
- * Locate exactly one account matching the name or name/email string.
- *
- * @param nameOrEmail a string of the format
- * "Full Name &lt;email@example&gt;", or just the email address
- * ("email@example"), or a full name, or an account id.
- * @return the single account that matches; null if no account matches or
- * there are multiple candidates.
- */
- public Account find(final String nameOrEmail) throws OrmException {
- if (nameOrEmail.matches("^[1-9][0-9]*$")) {
- return byId.get(Account.Id.parse(nameOrEmail)).getAccount();
- }
-
- final int lt = nameOrEmail.indexOf('<');
- final int gt = nameOrEmail.indexOf('>');
- if (lt >= 0 && gt > lt && nameOrEmail.contains("@")) {
- return findByEmail(nameOrEmail.substring(lt + 1, gt));
- }
-
- if (nameOrEmail.contains("@")) {
- return findByEmail(nameOrEmail);
- }
-
- final Account.Id id = realm.lookup(nameOrEmail);
- if (id != null) {
- return byId.get(id).getAccount();
- }
-
- return oneAccount(schema.get().accounts().byFullName(nameOrEmail));
- }
-
- private Account findByEmail(final String email) {
- final Set<Account.Id> candidates = byEmail.get(email);
- if (1 == candidates.size()) {
- return byId.get(candidates.iterator().next()).getAccount();
- }
- return null;
- }
-
- private static Account oneAccount(final ResultSet<Account> rs) {
- final List<Account> r = rs.toList();
- return r.size() == 1 ? r.get(0) : null;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AccountState.java b/src/main/java/com/google/gerrit/server/account/AccountState.java
deleted file mode 100644
index 2210cff911..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AccountState.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-public class AccountState {
- private final Account account;
- private final Set<AccountGroup.Id> internalGroups;
- private final Collection<AccountExternalId> externalIds;
-
- public AccountState(final Account account,
- final Set<AccountGroup.Id> actualGroups,
- final Collection<AccountExternalId> externalIds) {
- this.account = account;
- this.internalGroups = actualGroups;
- this.externalIds = externalIds;
- }
-
- /** Get the cached account metadata. */
- public Account getAccount() {
- return account;
- }
-
- /**
- * All email addresses registered to this account.
- * <p>
- * Gerrit is "reasonably certain" that the returned email addresses actually
- * belong to the user of the account. Some emails may have been obtained from
- * the authentication provider, which in the case of OpenID may be trusting
- * the provider to have validated the address. Other emails may have been
- * validated by Gerrit directly.
- */
- public Set<String> getEmailAddresses() {
- final Set<String> emails = new HashSet<String>();
- for (final AccountExternalId e : externalIds) {
- if (e.getEmailAddress() != null && !e.getEmailAddress().isEmpty()) {
- emails.add(e.getEmailAddress());
- }
- }
- return emails;
- }
-
- /** The external identities that identify the account holder. */
- public Collection<AccountExternalId> getExternalIds() {
- return externalIds;
- }
-
- /** The set of groups maintained directly within the Gerrit database. */
- public Set<AccountGroup.Id> getInternalGroups() {
- return internalGroups;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AuthRequest.java b/src/main/java/com/google/gerrit/server/account/AuthRequest.java
deleted file mode 100644
index f554f8b326..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AuthRequest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import static com.google.gerrit.client.reviewdb.AccountExternalId.SCHEME_GERRIT;
-import static com.google.gerrit.client.reviewdb.AccountExternalId.SCHEME_MAILTO;
-
-/**
- * Information for {@link AccountManager#authenticate(AuthRequest)}.
- * <p>
- * Callers should populate this object with as much information as possible
- * about the user account. For example, OpenID authentication might return
- * registration information including a display name for the user, and an email
- * address for them. These fields however are optional, as not all OpenID
- * providers return them, and not all non-OpenID systems can use them.
- */
-public class AuthRequest {
- /** Create a request for a local username, such as from LDAP. */
- public static AuthRequest forUser(final String username) {
- final AuthRequest r;
- r = new AuthRequest(SCHEME_GERRIT + username);
- r.setSshUserName(username);
- return r;
- }
-
- /**
- * Create a request for an email address registration.
- * <p>
- * This type of request should be used only to attach a new email address to
- * an existing user account.
- */
- public static AuthRequest forEmail(final String email) {
- final AuthRequest r;
- r = new AuthRequest(SCHEME_MAILTO + email);
- r.setEmailAddress(email);
- return r;
- }
-
- private final String externalId;
- private String password;
- private String displayName;
- private String emailAddress;
- private String sshUserName;
-
- public AuthRequest(final String externalId) {
- this.externalId = externalId;
- }
-
- public String getExternalId() {
- return externalId;
- }
-
- public boolean isScheme(final String scheme) {
- return getExternalId().startsWith(scheme);
- }
-
- public String getLocalUser() {
- if (isScheme(SCHEME_GERRIT)) {
- return getExternalId().substring(SCHEME_GERRIT.length());
- }
- return null;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(final String pass) {
- password = pass;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public void setDisplayName(final String name) {
- displayName = name != null && name.length() > 0 ? name : null;
- }
-
- public String getEmailAddress() {
- return emailAddress;
- }
-
- public void setEmailAddress(final String email) {
- emailAddress = email != null && email.length() > 0 ? email : null;
- }
-
- public String getSshUserName() {
- return sshUserName;
- }
-
- public void setSshUserName(final String user) {
- sshUserName = user;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/AuthResult.java b/src/main/java/com/google/gerrit/server/account/AuthResult.java
deleted file mode 100644
index 84b030c541..0000000000
--- a/src/main/java/com/google/gerrit/server/account/AuthResult.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Result from {@link AccountManager#authenticate(AuthRequest)}. */
-public class AuthResult {
- private final Account.Id accountId;
- private final boolean isNew;
-
- AuthResult(final Account.Id accountId, final boolean isNew) {
- this.accountId = accountId;
- this.isNew = isNew;
- }
-
- /** Identity of the user account that was authenticated into. */
- public Account.Id getAccountId() {
- return accountId;
- }
-
- /**
- * True if this account was recently created for the user.
- * <p>
- * New users should be redirected to the registration screen, so they can
- * configure their new user account.
- */
- public boolean isNew() {
- return isNew;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
deleted file mode 100644
index efd6df8a0c..0000000000
--- a/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.inject.Inject;
-
-import java.util.Collections;
-import java.util.Set;
-
-public final class DefaultRealm implements Realm {
- private final EmailExpander emailExpander;
- private final AccountByEmailCache byEmail;
-
- @Inject
- DefaultRealm(final EmailExpander emailExpander,
- final AccountByEmailCache byEmail) {
- this.emailExpander = emailExpander;
- this.byEmail = byEmail;
- }
-
- @Override
- public boolean allowsEdit(final Account.FieldName field) {
- return true;
- }
-
- @Override
- public AuthRequest authenticate(final AuthRequest who) {
- if (who.getEmailAddress() == null && who.getLocalUser() != null
- && emailExpander.canExpand(who.getLocalUser())) {
- who.setEmailAddress(emailExpander.expand(who.getLocalUser()));
- }
- return who;
- }
-
- @Override
- public void onCreateAccount(final AuthRequest who, final Account account) {
- }
-
- @Override
- public Set<AccountGroup.Id> groups(final AccountState who) {
- return who.getInternalGroups();
- }
-
- @Override
- public Account.Id lookup(final String accountName) {
- if (emailExpander.canExpand(accountName)) {
- final Set<Account.Id> c = byEmail.get(emailExpander.expand(accountName));
- if (1 == c.size()) {
- return c.iterator().next();
- }
- }
- return null;
- }
-
- @Override
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
- return Collections.emptySet();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/GroupCache.java b/src/main/java/com/google/gerrit/server/account/GroupCache.java
deleted file mode 100644
index 1a2ac2a0db..0000000000
--- a/src/main/java/com/google/gerrit/server/account/GroupCache.java
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-
-/** Tracks group objects in memory for efficient access. */
-public interface GroupCache {
- public AccountGroup get(AccountGroup.Id groupId);
-
- public AccountGroup get(AccountGroup.ExternalNameKey externalName);
-
- public void evict(AccountGroup group);
-
- public void evictAfterRename(AccountGroup.NameKey oldName);
-
- public AccountGroup lookup(String groupName);
-}
diff --git a/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
deleted file mode 100644
index f49824f135..0000000000
--- a/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-/** Tracks group objects in memory for efficient access. */
-@Singleton
-public class GroupCacheImpl implements GroupCache {
- private static final String CACHE_NAME = "groups";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<com.google.gwtorm.client.Key<?>, AccountGroup>> byId =
- new TypeLiteral<Cache<com.google.gwtorm.client.Key<?>, AccountGroup>>() {};
- core(byId, CACHE_NAME);
- bind(GroupCacheImpl.class);
- bind(GroupCache.class).to(GroupCacheImpl.class);
- }
- };
- }
-
- private final SchemaFactory<ReviewDb> schema;
- private final AccountGroup.Id administrators;
- private final SelfPopulatingCache<AccountGroup.Id, AccountGroup> byId;
- private final SelfPopulatingCache<AccountGroup.NameKey, AccountGroup> byName;
- private final SelfPopulatingCache<AccountGroup.ExternalNameKey, AccountGroup> byExternalName;
-
- @Inject
- GroupCacheImpl(
- final SchemaFactory<ReviewDb> sf,
- final AuthConfig authConfig,
- @Named(CACHE_NAME) final Cache<com.google.gwtorm.client.Key<?>, AccountGroup> rawAny) {
- schema = sf;
- administrators = authConfig.getAdministratorsGroup();
-
- byId =
- new SelfPopulatingCache<AccountGroup.Id, AccountGroup>((Cache) rawAny) {
- @Override
- public AccountGroup createEntry(final AccountGroup.Id key)
- throws Exception {
- return lookup(key);
- }
-
- @Override
- protected AccountGroup missing(final AccountGroup.Id key) {
- return missingGroup(key);
- }
- };
-
- byName =
- new SelfPopulatingCache<AccountGroup.NameKey, AccountGroup>(
- (Cache) rawAny) {
- @Override
- public AccountGroup createEntry(final AccountGroup.NameKey key)
- throws Exception {
- return lookup(key);
- }
- };
-
- byExternalName =
- new SelfPopulatingCache<AccountGroup.ExternalNameKey, AccountGroup>(
- (Cache) rawAny) {
- @Override
- public AccountGroup createEntry(final AccountGroup.ExternalNameKey key)
- throws Exception {
- return lookup(key);
- }
- };
- }
-
- private AccountGroup lookup(final AccountGroup.Id groupId)
- throws OrmException {
- final ReviewDb db = schema.open();
- try {
- final AccountGroup group = db.accountGroups().get(groupId);
- if (group != null) {
- return group;
- } else {
- return missingGroup(groupId);
- }
- } finally {
- db.close();
- }
- }
-
- private AccountGroup missingGroup(final AccountGroup.Id groupId) {
- final AccountGroup.NameKey name =
- new AccountGroup.NameKey("Deleted Group" + groupId.toString());
- final AccountGroup g = new AccountGroup(name, groupId);
- g.setType(AccountGroup.Type.SYSTEM);
- g.setOwnerGroupId(administrators);
- return g;
- }
-
- private AccountGroup lookup(final AccountGroup.NameKey groupName)
- throws OrmException {
- final ReviewDb db = schema.open();
- try {
- return db.accountGroups().get(groupName);
- } finally {
- db.close();
- }
- }
-
- private AccountGroup lookup(final AccountGroup.ExternalNameKey externalName)
- throws OrmException {
- final ReviewDb db = schema.open();
- try {
- return db.accountGroups().get(externalName);
- } finally {
- db.close();
- }
- }
-
- public AccountGroup get(final AccountGroup.Id groupId) {
- return byId.get(groupId);
- }
-
- public void evict(final AccountGroup group) {
- byId.remove(group.getId());
- byName.remove(group.getNameKey());
- byExternalName.remove(group.getExternalNameKey());
- }
-
- public void evictAfterRename(final AccountGroup.NameKey oldName) {
- byName.remove(oldName);
- }
-
- public AccountGroup lookup(final String groupName) {
- return byName.get(new AccountGroup.NameKey(groupName));
- }
-
- public AccountGroup get(final AccountGroup.ExternalNameKey externalName) {
- return byExternalName.get(externalName);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/GroupControl.java b/src/main/java/com/google/gerrit/server/account/GroupControl.java
deleted file mode 100644
index e3284cdc3b..0000000000
--- a/src/main/java/com/google/gerrit/server/account/GroupControl.java
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.server.CurrentUser;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Access control management for a group of accounts managed in Gerrit. */
-public class GroupControl {
- public static class Factory {
- private final GroupCache groupCache;
- private final Provider<CurrentUser> user;
-
- @Inject
- Factory(final GroupCache gc, final Provider<CurrentUser> cu) {
- groupCache = gc;
- user = cu;
- }
-
- public GroupControl controlFor(final AccountGroup.Id groupId)
- throws NoSuchGroupException {
- final AccountGroup group = groupCache.get(groupId);
- if (group == null) {
- throw new NoSuchGroupException(groupId);
- }
- return new GroupControl(user.get(), group);
- }
-
- public GroupControl validateFor(final AccountGroup.Id groupId)
- throws NoSuchGroupException {
- final GroupControl c = controlFor(groupId);
- if (!c.isVisible()) {
- throw new NoSuchGroupException(groupId);
- }
- return c;
- }
- }
-
- private final CurrentUser user;
- private final AccountGroup group;
-
- GroupControl(final CurrentUser who, final AccountGroup gc) {
- user = who;
- group = gc;
- }
-
- public CurrentUser getCurrentUser() {
- return user;
- }
-
- public AccountGroup getAccountGroup() {
- return group;
- }
-
- /** Can this user see this group exists? */
- public boolean isVisible() {
- return isOwner();
- }
-
- public boolean isOwner() {
- final AccountGroup.Id owner = group.getOwnerGroupId();
- return getCurrentUser().getEffectiveGroups().contains(owner)
- || getCurrentUser().isAdministrator();
- }
-
- public boolean canAdd(final Account.Id id) {
- return isOwner();
- }
-
- public boolean canRemove(final Account.Id id) {
- return isOwner();
- }
-
- public boolean canSee(Account.Id id) {
- return isOwner();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java b/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java
deleted file mode 100644
index ce1b3935da..0000000000
--- a/src/main/java/com/google/gerrit/server/account/NoSuchGroupException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-
-/** Indicates the account group does not exist. */
-public class NoSuchGroupException extends Exception {
- public NoSuchGroupException(final AccountGroup.Id key) {
- this(key, null);
- }
-
- public NoSuchGroupException(final AccountGroup.Id key, final Throwable why) {
- super(key.toString(), why);
- }
-
- public NoSuchGroupException(final AccountGroup.NameKey k) {
- this(k, null);
- }
-
- public NoSuchGroupException(final AccountGroup.NameKey k, final Throwable why) {
- super(k.toString(), why);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/account/Realm.java b/src/main/java/com/google/gerrit/server/account/Realm.java
deleted file mode 100644
index 9b57087e42..0000000000
--- a/src/main/java/com/google/gerrit/server/account/Realm.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.account;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-
-import java.util.Set;
-
-public interface Realm {
- /** Can the end-user modify this field of their own account? */
- public boolean allowsEdit(Account.FieldName field);
-
- public AuthRequest authenticate(AuthRequest who) throws AccountException;
-
- public void onCreateAccount(AuthRequest who, Account account);
-
- public Set<AccountGroup.Id> groups(AccountState who);
-
- /**
- * Locate an account whose local username is the given account name.
- * <p>
- * Generally this only works for local realms, such as one backed by an LDAP
- * directory, or where there is an {@link EmailExpander} configured that knows
- * how to convert the accountName into an email address, and then locate the
- * user by that email address.
- */
- public Account.Id lookup(String accountName);
-
- /**
- * Search for matching external groups.
- */
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name);
-}
diff --git a/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java b/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
deleted file mode 100644
index d8a20110be..0000000000
--- a/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-class ApprovalTypesProvider implements Provider<ApprovalTypes> {
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- ApprovalTypesProvider(final SchemaFactory<ReviewDb> sf) {
- schema = sf;
- }
-
- @Override
- public ApprovalTypes get() {
- List<ApprovalType> approvalTypes = new ArrayList<ApprovalType>(2);
- List<ApprovalType> actionTypes = new ArrayList<ApprovalType>(2);
-
- try {
- final ReviewDb db = schema.open();
- try {
- for (final ApprovalCategory c : db.approvalCategories().all()) {
- final List<ApprovalCategoryValue> values =
- db.approvalCategoryValues().byCategory(c.getId()).toList();
- final ApprovalType type = new ApprovalType(c, values);
- if (type.getCategory().isAction()) {
- actionTypes.add(type);
- } else {
- approvalTypes.add(type);
- }
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new ProvisionException("Cannot query approval categories", e);
- }
-
- approvalTypes = Collections.unmodifiableList(approvalTypes);
- actionTypes = Collections.unmodifiableList(actionTypes);
- return new ApprovalTypes(approvalTypes, actionTypes);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/src/main/java/com/google/gerrit/server/config/AuthConfig.java
deleted file mode 100644
index 0756c645b0..0000000000
--- a/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AuthType;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gwtjsonrpc.server.SignedToken;
-import com.google.gwtjsonrpc.server.XsrfException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.Config;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/** Authentication related settings from {@code gerrit.config}. */
-@Singleton
-public class AuthConfig {
- private final AuthType authType;
- private final String httpHeader;
- private final String logoutUrl;
- private final String[] trusted;
- private final SignedToken emailReg;
-
- private final AccountGroup.Id administratorGroup;
- private final Set<AccountGroup.Id> anonymousGroups;
- private final Set<AccountGroup.Id> registeredGroups;
-
- private final boolean allowGoogleAccountUpgrade;
-
- @Inject
- AuthConfig(@GerritServerConfig final Config cfg, final SystemConfig s)
- throws XsrfException {
- authType = toType(cfg);
- httpHeader = cfg.getString("auth", null, "httpheader");
- logoutUrl = cfg.getString("auth", null, "logouturl");
- trusted = toTrusted(cfg);
- emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey);
-
- final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(2);
- r.add(s.anonymousGroupId);
- r.add(s.registeredGroupId);
- registeredGroups = Collections.unmodifiableSet(r);
- anonymousGroups = Collections.singleton(s.anonymousGroupId);
- administratorGroup = s.adminGroupId;
-
- if (authType == AuthType.OPENID) {
- allowGoogleAccountUpgrade =
- cfg.getBoolean("auth", "allowgoogleaccountupgrade", false);
- } else {
- allowGoogleAccountUpgrade = false;
- }
- }
-
- private String[] toTrusted(final Config cfg) {
- final String[] r = cfg.getStringList("auth", null, "trustedopenid");
- if (r.length == 0) {
- return new String[] {"http://", "https://"};
- }
- return r;
- }
-
- private static AuthType toType(final Config cfg) {
- if (isBecomeAnyoneEnabled()) {
- return AuthType.DEVELOPMENT_BECOME_ANY_ACCOUNT;
- }
- return ConfigUtil.getEnum(cfg, "auth", null, "type", AuthType.OPENID);
- }
-
- private static boolean isBecomeAnyoneEnabled() {
- try {
- String s = "com.google.gerrit.server.http.BecomeAnyAccountLoginServlet";
- return Boolean.getBoolean(s);
- } catch (SecurityException se) {
- return false;
- }
- }
-
- /** Type of user authentication used by this Gerrit server. */
- public AuthType getAuthType() {
- return authType;
- }
-
- public String getLoginHttpHeader() {
- return httpHeader;
- }
-
- public String getLogoutURL() {
- return logoutUrl;
- }
-
- public SignedToken getEmailRegistrationToken() {
- return emailReg;
- }
-
- public boolean isAllowGoogleAccountUpgrade() {
- return allowGoogleAccountUpgrade;
- }
-
- /** Identity of the magic group with full powers. */
- public AccountGroup.Id getAdministratorsGroup() {
- return administratorGroup;
- }
-
- /** Groups that all users, including anonymous users, belong to. */
- public Set<AccountGroup.Id> getAnonymousGroups() {
- return anonymousGroups;
- }
-
- /** Groups that all users who have created an account belong to. */
- public Set<AccountGroup.Id> getRegisteredGroups() {
- return registeredGroups;
- }
-
- public boolean isIdentityTrustable(final Collection<AccountExternalId> ids) {
- switch (getAuthType()) {
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- case HTTP:
- case HTTP_LDAP:
- case LDAP:
- // Its safe to assume yes for an HTTP authentication type, as the
- // only way in is through some external system that the admin trusts
- //
- return true;
-
- case OPENID:
- // All identities must be trusted in order to trust the account.
- //
- for (final AccountExternalId e : ids) {
- if (!isTrusted(e)) {
- return false;
- }
- }
- return true;
-
- default:
- // Assume not, we don't understand the login format.
- //
- return false;
- }
- }
-
- private boolean isTrusted(final AccountExternalId id) {
- if (id.isScheme(AccountExternalId.LEGACY_GAE)) {
- // Assume this is a trusted token, its a legacy import from
- // a fairly well respected provider and only takes effect if
- // the administrator has the import still enabled
- //
- return isAllowGoogleAccountUpgrade();
- }
-
- if (id.isScheme(AccountExternalId.SCHEME_MAILTO)) {
- // mailto identities are created by sending a unique validation
- // token to the address and asking them to come back to the site
- // with that token.
- //
- return true;
- }
-
- for (final String p : trusted) {
- if (matches(p, id)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean matches(final String p, final AccountExternalId id) {
- if (p.startsWith("^") && p.endsWith("$")) {
- return id.getExternalId().matches(p);
-
- } else {
- return id.getExternalId().startsWith(p);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/DatabaseModule.java b/src/main/java/com/google/gerrit/server/config/DatabaseModule.java
deleted file mode 100644
index f59db12e30..0000000000
--- a/src/main/java/com/google/gerrit/server/config/DatabaseModule.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.gwtorm.jdbc.Database;
-import com.google.inject.Key;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Names;
-
-import javax.sql.DataSource;
-
-/** Loads the database with standard dependencies. */
-public class DatabaseModule extends FactoryModule {
- public static final Key<DataSource> DS =
- Key.get(DataSource.class, Names.named("ReviewDb"));
-
- @Override
- protected void configure() {
- bind(DS).toProvider(ReviewDbDataSourceProvider.class).in(SINGLETON);
-
- bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).to(
- new TypeLiteral<Database<ReviewDb>>() {}).in(SINGLETON);
- bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(
- ReviewDbDatabaseProvider.class).in(SINGLETON);
-
- bind(SystemConfig.class).toProvider(SystemConfigProvider.class).in(
- SINGLETON);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java b/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java
deleted file mode 100644
index 43a094499d..0000000000
--- a/src/main/java/com/google/gerrit/server/config/GerritConfigModule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.inject.AbstractModule;
-
-import org.eclipse.jgit.lib.Config;
-
-import java.io.File;
-
-/** Injection for the really primitive configuration data. */
-public class GerritConfigModule extends AbstractModule {
- @Override
- protected void configure() {
- bind(File.class).annotatedWith(SitePath.class).toProvider(
- SitePathProvider.class).in(SINGLETON);
- bind(Project.NameKey.class).annotatedWith(WildProjectName.class)
- .toProvider(WildProjectNameProvider.class).in(SINGLETON);
- bind(Config.class).annotatedWith(GerritServerConfig.class).toProvider(
- GerritServerConfigProvider.class).in(SINGLETON);
- bind(AuthConfig.class).in(SINGLETON);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
deleted file mode 100644
index 83a16ce821..0000000000
--- a/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import static com.google.inject.Scopes.SINGLETON;
-import static com.google.inject.Stage.PRODUCTION;
-
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AuthType;
-import com.google.gerrit.git.ChangeMergeQueue;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.git.MergeOp;
-import com.google.gerrit.git.MergeQueue;
-import com.google.gerrit.git.PatchSetImporter;
-import com.google.gerrit.git.PushAllProjectsOp;
-import com.google.gerrit.git.PushReplication;
-import com.google.gerrit.git.ReloadSubmitQueueOp;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.git.WorkQueue;
-import com.google.gerrit.server.AnonymousUser;
-import com.google.gerrit.server.FileTypeRegistry;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.GerritPersonIdentProvider;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.MimeUtilFileTypeRegistry;
-import com.google.gerrit.server.ReplicationUser;
-import com.google.gerrit.server.account.AccountByEmailCacheImpl;
-import com.google.gerrit.server.account.AccountCacheImpl;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.account.DefaultRealm;
-import com.google.gerrit.server.account.EmailExpander;
-import com.google.gerrit.server.account.GroupCacheImpl;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.ldap.LdapModule;
-import com.google.gerrit.server.mail.AbandonedSender;
-import com.google.gerrit.server.mail.CommentSender;
-import com.google.gerrit.server.mail.EmailSender;
-import com.google.gerrit.server.mail.FromAddressGenerator;
-import com.google.gerrit.server.mail.FromAddressGeneratorProvider;
-import com.google.gerrit.server.mail.MergeFailSender;
-import com.google.gerrit.server.mail.MergedSender;
-import com.google.gerrit.server.mail.RegisterNewEmailSender;
-import com.google.gerrit.server.mail.SmtpEmailSender;
-import com.google.gerrit.server.patch.PatchListCacheImpl;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.project.ProjectCacheImpl;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.ssh.SshKeyCacheImpl;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.inject.Guice;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/** Starts global state with standard dependencies. */
-public class GerritGlobalModule extends FactoryModule {
- public static Injector createInjector() {
- final Injector db = Guice.createInjector(PRODUCTION, new DatabaseModule());
- final CanonicalWebUrlModule canonicalWebUrl = new CanonicalWebUrlModule() {
- @Override
- protected Class<? extends Provider<String>> provider() {
- return CanonicalWebUrlProvider.class;
- }
- };
- return createInjector(db, canonicalWebUrl);
- }
-
- public static Injector createInjector(final Injector db,
- final CanonicalWebUrlModule canonicalWebUrl) {
- final Injector cfg = db.createChildInjector(new GerritConfigModule());
- final List<Module> modules = new ArrayList<Module>();
- modules.add(cfg.getInstance(GerritGlobalModule.class));
- modules.add(canonicalWebUrl);
- return cfg.createChildInjector(modules);
- }
-
- private final AuthType loginType;
-
- @Inject
- GerritGlobalModule(final AuthConfig authConfig,
- @GerritServerConfig final Config config) {
- loginType = authConfig.getAuthType();
- }
-
- @Override
- protected void configure() {
- switch (loginType) {
- case HTTP_LDAP:
- case LDAP:
- install(new LdapModule());
- break;
-
- default:
- bind(Realm.class).to(DefaultRealm.class);
- break;
- }
-
- bind(ApprovalTypes.class).toProvider(ApprovalTypesProvider.class).in(
- SINGLETON);
- bind(EmailExpander.class).toProvider(EmailExpanderProvider.class).in(
- SINGLETON);
- bind(AnonymousUser.class);
-
- bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider(
- GerritPersonIdentProvider.class);
-
- bind(CachePool.class);
- install(AccountByEmailCacheImpl.module());
- install(AccountCacheImpl.module());
- install(GroupCacheImpl.module());
- install(PatchListCacheImpl.module());
- install(ProjectCacheImpl.module());
- install(SshKeyCacheImpl.module());
-
- factory(AccountInfoCacheFactory.Factory.class);
- factory(ProjectState.Factory.class);
-
- bind(GitRepositoryManager.class);
- bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class);
- bind(WorkQueue.class);
-
- bind(ReplicationQueue.class).to(PushReplication.class).in(SINGLETON);
- factory(PushAllProjectsOp.Factory.class);
-
- bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
- factory(MergeOp.Factory.class);
- factory(ReloadSubmitQueueOp.Factory.class);
-
- bind(FromAddressGenerator.class).toProvider(
- FromAddressGeneratorProvider.class).in(SINGLETON);
- bind(EmailSender.class).to(SmtpEmailSender.class).in(SINGLETON);
-
- factory(PatchSetImporter.Factory.class);
- bind(PatchSetInfoFactory.class);
- bind(IdentifiedUser.GenericFactory.class).in(SINGLETON);
- factory(FunctionState.Factory.class);
-
- factory(AbandonedSender.Factory.class);
- factory(CommentSender.Factory.class);
- factory(MergedSender.Factory.class);
- factory(MergeFailSender.Factory.class);
- factory(RegisterNewEmailSender.Factory.class);
- factory(ReplicationUser.Factory.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
deleted file mode 100644
index 171b418037..0000000000
--- a/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.RequestCleanup;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.mail.AddReviewerSender;
-import com.google.gerrit.server.mail.CreateChangeSender;
-import com.google.gerrit.server.mail.ReplacePatchSetSender;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.inject.servlet.RequestScoped;
-
-/** Bindings for {@link RequestScoped} entities. */
-public class GerritRequestModule extends FactoryModule {
- @Override
- protected void configure() {
- bind(RequestCleanup.class).in(RequestScoped.class);
- bind(ReviewDb.class).toProvider(RequestScopedReviewDbProvider.class).in(
- RequestScoped.class);
- bind(IdentifiedUser.RequestFactory.class).in(SINGLETON);
- bind(AccountResolver.class);
-
- bind(ChangeControl.Factory.class).in(SINGLETON);
- bind(GroupControl.Factory.class).in(SINGLETON);
- bind(ProjectControl.Factory.class).in(SINGLETON);
-
- // Not really per-request, but dammit, I don't know where else to
- // easily park this stuff.
- //
- factory(AddReviewerSender.Factory.class);
- factory(CreateChangeSender.Factory.class);
- factory(ReplacePatchSetSender.Factory.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java b/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
deleted file mode 100644
index 69521fc59b..0000000000
--- a/src/main/java/com/google/gerrit/server/config/RequestScopedReviewDbProvider.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.RequestCleanup;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.Database;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-
-/** Provides {@link ReviewDb} database handle live only for this request. */
-@Singleton
-final class RequestScopedReviewDbProvider implements Provider<ReviewDb> {
- private final Database<ReviewDb> schema;
- private final Provider<RequestCleanup> cleanup;
-
- @Inject
- RequestScopedReviewDbProvider(final Database<ReviewDb> schema,
- final Provider<RequestCleanup> cleanup) {
- this.schema = schema;
- this.cleanup = cleanup;
- }
-
- @Override
- public ReviewDb get() {
- final ReviewDb c;
- try {
- c = schema.open();
- } catch (OrmException e) {
- throw new ProvisionException("Cannot open ReviewDb", e);
- }
- try {
- cleanup.get().add(new Runnable() {
- @Override
- public void run() {
- c.close();
- }
- });
- return c;
- } catch (Error e) {
- c.close();
- throw e;
- } catch (RuntimeException e) {
- c.close();
- throw e;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java b/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java
deleted file mode 100644
index 06805db77d..0000000000
--- a/src/main/java/com/google/gerrit/server/config/ReviewDbDatabaseProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.jdbc.Database;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.name.Named;
-
-import javax.sql.DataSource;
-
-/** Provides the {@code Database<ReviewDb>} database handle. */
-final class ReviewDbDatabaseProvider implements Provider<Database<ReviewDb>> {
- private final DataSource datasource;
-
- @Inject
- ReviewDbDatabaseProvider(@Named("ReviewDb") final DataSource ds) {
- datasource = ds;
- }
-
- @Override
- public Database<ReviewDb> get() {
- try {
- return new Database<ReviewDb>(datasource, ReviewDb.class);
- } catch (OrmException e) {
- throw new ProvisionException("Cannot create ReviewDb", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/SitePathProvider.java b/src/main/java/com/google/gerrit/server/config/SitePathProvider.java
deleted file mode 100644
index 4965f1b049..0000000000
--- a/src/main/java/com/google/gerrit/server/config/SitePathProvider.java
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.io.File;
-
-/** Provides {@link java.io.File} annotated with {@link SitePath}. */
-public class SitePathProvider implements Provider<File> {
- private final File path;
-
- @Inject
- SitePathProvider(final SystemConfig config) {
- final String p = config.sitePath;
- path = new File(p != null && p.length() > 0 ? p : ".").getAbsoluteFile();
- }
-
- @Override
- public File get() {
- return path;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java b/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java
deleted file mode 100644
index ab5a2933f0..0000000000
--- a/src/main/java/com/google/gerrit/server/config/SystemConfigProvider.java
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.SchemaVersion;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gerrit.server.workflow.NoOpFunction;
-import com.google.gerrit.server.workflow.SubmitFunction;
-import com.google.gwtjsonrpc.server.SignedToken;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Loads the {@link SystemConfig} from the database. */
-public class SystemConfigProvider implements Provider<SystemConfig> {
- private static final Project.NameKey DEFAULT_WILD_NAME =
- new Project.NameKey("-- All Projects --");
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- public SystemConfigProvider(final SchemaFactory<ReviewDb> sf) {
- schema = sf;
- }
-
- @Override
- public SystemConfig get() {
- try {
- final ReviewDb db = schema.open();
- try {
- SchemaVersion sVer = getSchemaVersion(db);
-
- if (sVer == null) {
- // Assume the schema is empty and try to populate it.
- //
- sVer = createSchema(db);
- }
-
- switch (sVer.versionNbr) {
- case 2:
- initPushTagCategory(db);
- initPushUpdateBranchCategory(db);
-
- sVer.versionNbr = 3;
- db.schemaVersion().update(Collections.singleton(sVer));
- break;
-
- case 15:
- sVer.versionNbr = 16;
- db.schemaVersion().update(Collections.singleton(sVer));
- break;
- }
-
- if (sVer.versionNbr != ReviewDb.VERSION) {
- throw new OrmException("Unsupported schema version "
- + sVer.versionNbr + "; expected schema version "
- + ReviewDb.VERSION);
- }
-
- final List<SystemConfig> all = db.systemConfig().all().toList();
- switch (all.size()) {
- case 1:
- return all.get(0);
- case 0:
- throw new OrmException("system_config table is empty");
- default:
- throw new OrmException("system_config must have exactly 1 row;"
- + " found " + all.size() + " rows instead");
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new ProvisionException("Cannot read system_config", e);
- }
- }
-
- private SchemaVersion createSchema(final ReviewDb db) throws OrmException {
- db.createSchema();
-
- final SchemaVersion sVer = SchemaVersion.create();
- sVer.versionNbr = ReviewDb.VERSION;
- db.schemaVersion().insert(Collections.singleton(sVer));
-
- final SystemConfig sConfig = initSystemConfig(db);
- initOwnerCategory(db);
- initReadCategory(db, sConfig);
- initVerifiedCategory(db);
- initCodeReviewCategory(db, sConfig);
- initSubmitCategory(db);
- initPushTagCategory(db);
- initPushUpdateBranchCategory(db);
- initWildCardProject(db);
-
- return sVer;
- }
-
- private SchemaVersion getSchemaVersion(final ReviewDb db) {
- try {
- return db.schemaVersion().get(new SchemaVersion.Key());
- } catch (OrmException e) {
- return null;
- }
- }
-
- private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
- final AccountGroup admin =
- new AccountGroup(new AccountGroup.NameKey("Administrators"),
- new AccountGroup.Id(c.nextAccountGroupId()));
- admin.setDescription("Gerrit Site Administrators");
- admin.setType(AccountGroup.Type.INTERNAL);
- c.accountGroups().insert(Collections.singleton(admin));
-
- final AccountGroup anonymous =
- new AccountGroup(new AccountGroup.NameKey("Anonymous Users"),
- new AccountGroup.Id(c.nextAccountGroupId()));
- anonymous.setDescription("Any user, signed-in or not");
- anonymous.setOwnerGroupId(admin.getId());
- anonymous.setType(AccountGroup.Type.SYSTEM);
- c.accountGroups().insert(Collections.singleton(anonymous));
-
- final AccountGroup registered =
- new AccountGroup(new AccountGroup.NameKey("Registered Users"),
- new AccountGroup.Id(c.nextAccountGroupId()));
- registered.setDescription("Any signed-in user");
- registered.setOwnerGroupId(admin.getId());
- registered.setType(AccountGroup.Type.SYSTEM);
- c.accountGroups().insert(Collections.singleton(registered));
-
- File sitePath = new File(".").getAbsoluteFile();
- if (".".equals(sitePath.getName())) {
- sitePath = sitePath.getParentFile();
- }
-
- final SystemConfig s = SystemConfig.create();
- s.registerEmailPrivateKey = SignedToken.generateRandomKey();
- s.adminGroupId = admin.getId();
- s.anonymousGroupId = anonymous.getId();
- s.registeredGroupId = registered.getId();
- s.sitePath = sitePath.getAbsolutePath();
- c.systemConfig().insert(Collections.singleton(s));
- return s;
- }
-
- private void initWildCardProject(final ReviewDb c) throws OrmException {
- final Project p;
-
- p = new Project(DEFAULT_WILD_NAME, WildProjectNameProvider.WILD_PROJECT_ID);
- p.setDescription("Rights inherited by all other projects");
- p.setUseContributorAgreements(false);
- c.projects().insert(Collections.singleton(p));
- }
-
- private void initVerifiedCategory(final ReviewDb c) throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(new ApprovalCategory.Id("VRIF"), "Verified");
- cat.setPosition((short) 0);
- cat.setAbbreviatedName("V");
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 1, "Verified"));
- vals.add(value(cat, 0, "No score"));
- vals.add(value(cat, -1, "Fails"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- }
-
- private void initCodeReviewCategory(final ReviewDb c,
- final SystemConfig sConfig) throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(new ApprovalCategory.Id("CRVW"), "Code Review");
- cat.setPosition((short) 1);
- cat.setAbbreviatedName("R");
- cat.setCopyMinScore(true);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 2, "Looks good to me, approved"));
- vals.add(value(cat, 1, "Looks good to me, but someone else must approve"));
- vals.add(value(cat, 0, "No score"));
- vals.add(value(cat, -1, "I would prefer that you didn't submit this"));
- vals.add(value(cat, -2, "Do not submit"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
-
- final ProjectRight approve =
- new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
- sConfig.registeredGroupId));
- approve.setMaxValue((short) 1);
- approve.setMinValue((short) -1);
- c.projectRights().insert(Collections.singleton(approve));
- }
-
- private void initOwnerCategory(final ReviewDb c) throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 1, "Administer All Settings"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- }
-
- private void initReadCategory(final ReviewDb c, final SystemConfig sConfig)
- throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 2, "Upload permission"));
- vals.add(value(cat, 1, "Read access"));
- vals.add(value(cat, -1, "No access"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- {
- final ProjectRight read =
- new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
- sConfig.anonymousGroupId));
- read.setMaxValue((short) 1);
- read.setMinValue((short) 1);
- c.projectRights().insert(Collections.singleton(read));
- }
- {
- final ProjectRight read =
- new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
- sConfig.registeredGroupId));
- read.setMaxValue((short) 2);
- read.setMinValue((short) 1);
- c.projectRights().insert(Collections.singleton(read));
- }
- {
- final ProjectRight read =
- new ProjectRight(new ProjectRight.Key(DEFAULT_WILD_NAME, cat.getId(),
- sConfig.adminGroupId));
- read.setMaxValue((short) 1);
- read.setMinValue((short) 1);
- c.projectRights().insert(Collections.singleton(read));
- }
- }
-
- private void initSubmitCategory(final ReviewDb c) throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit");
- cat.setPosition((short) -1);
- cat.setFunctionName(SubmitFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, 1, "Submit"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- }
-
- private void initPushTagCategory(final ReviewDb c) throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Annotated Tag");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag"));
- vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED,
- "Create Annotated Tag"));
- vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANY, "Create Any Tag"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- }
-
- private void initPushUpdateBranchCategory(final ReviewDb c)
- throws OrmException {
- final Transaction txn = c.beginTransaction();
- final ApprovalCategory cat;
- final ArrayList<ApprovalCategoryValue> vals;
-
- cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch");
- cat.setPosition((short) -1);
- cat.setFunctionName(NoOpFunction.NAME);
- vals = new ArrayList<ApprovalCategoryValue>();
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch"));
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch"));
- vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE,
- "Force Push Branch; Delete Branch"));
- c.approvalCategories().insert(Collections.singleton(cat), txn);
- c.approvalCategoryValues().insert(vals, txn);
- txn.commit();
- }
-
- private static ApprovalCategoryValue value(final ApprovalCategory cat,
- final int value, final String name) {
- return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(),
- (short) value), name);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/config/WildProjectName.java b/src/main/java/com/google/gerrit/server/config/WildProjectName.java
deleted file mode 100644
index 7c7bfa92dc..0000000000
--- a/src/main/java/com/google/gerrit/server/config/WildProjectName.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.inject.BindingAnnotation;
-
-import java.lang.annotation.Retention;
-
-/**
- * Marker on a {@link Project.NameKey} for the current wildcard project.
- * <p>
- * This is the name of the project whose rights inherit into every other project
- * in the system.
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface WildProjectName {
-}
diff --git a/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java b/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
deleted file mode 100644
index 1d67491de7..0000000000
--- a/src/main/java/com/google/gerrit/server/config/WildProjectNameProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-class WildProjectNameProvider implements Provider<Project.NameKey> {
- /** Project.Id meaning "any and all projects on this server". */
- static final Project.Id WILD_PROJECT_ID = new Project.Id(0);
-
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- WildProjectNameProvider(final SchemaFactory<ReviewDb> schema,
- /*
- * Unused, but we need to force it to load before we do, otherwise we risk
- * reading an empty database without the wild project being in the database.
- * Asking for it should ensures Guice loads it first.
- */
- final SystemConfig config) {
- this.schema = schema;
- }
-
- public Project.NameKey get() {
- try {
- final ReviewDb db = schema.open();
- try {
- final Project p = db.projects().get(WILD_PROJECT_ID);
- if (p == null) {
- throw new ProvisionException("No project " + WILD_PROJECT_ID);
- }
- return p.getNameKey();
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new ProvisionException("Cannot load " + WILD_PROJECT_ID, e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/contact/ContactStore.java b/src/main/java/com/google/gerrit/server/contact/ContactStore.java
deleted file mode 100644
index ebdf467c55..0000000000
--- a/src/main/java/com/google/gerrit/server/contact/ContactStore.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.contact;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.rpc.ContactInformationStoreException;
-
-public interface ContactStore {
- boolean isEnabled();
-
- void store(Account account, ContactInformation info)
- throws ContactInformationStoreException;
-}
diff --git a/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java b/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java
deleted file mode 100644
index 977d86093f..0000000000
--- a/src/main/java/com/google/gerrit/server/contact/ContactStoreProvider.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.contact;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.SitePath;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.eclipse.jgit.lib.Config;
-
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-/** Creates the {@link ContactStore} based on the configuration. */
-public class ContactStoreProvider implements Provider<ContactStore> {
- private final Config config;
- private final File sitePath;
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- ContactStoreProvider(@GerritServerConfig final Config config,
- @SitePath final File sitePath, final SchemaFactory<ReviewDb> schema) {
- this.config = config;
- this.sitePath = sitePath;
- this.schema = schema;
- }
-
- @Override
- public ContactStore get() {
- final String url = config.getString("contactstore", null, "url");
- if (url == null) {
- return new NoContactStore();
- }
-
- if (!havePGP()) {
- throw new ProvisionException("BouncyCastle PGP not installed; "
- + " needed to encrypt contact information");
- }
-
- final URL storeUrl;
- try {
- storeUrl = new URL(url);
- } catch (MalformedURLException e) {
- throw new ProvisionException("Invalid contactstore.url: " + url, e);
- }
-
- final String storeAPPSEC = config.getString("contactstore", null, "appsec");
- final File pubkey = new File(sitePath, "contact_information.pub");
- if (!pubkey.exists()) {
- throw new ProvisionException("PGP public key file \""
- + pubkey.getAbsolutePath() + "\" not found");
- }
- return new EncryptedContactStore(storeUrl, storeAPPSEC, pubkey, schema);
- }
-
- private static boolean havePGP() {
- try {
- Class.forName(PGPPublicKey.class.getName());
- return true;
- } catch (NoClassDefFoundError noBouncyCastle) {
- return false;
- } catch (ClassNotFoundException noBouncyCastle) {
- return false;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java b/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
deleted file mode 100644
index 74a533e37b..0000000000
--- a/src/main/java/com/google/gerrit/server/contact/EncryptedContactStore.java
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.contact;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.ContactInformationStoreException;
-import com.google.gerrit.server.UrlEncoded;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-
-import org.bouncycastle.bcpg.ArmoredOutputStream;
-import org.bouncycastle.openpgp.PGPCompressedData;
-import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
-import org.bouncycastle.openpgp.PGPEncryptedData;
-import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
-import org.bouncycastle.openpgp.PGPException;
-import org.bouncycastle.openpgp.PGPLiteralData;
-import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
-import org.bouncycastle.openpgp.PGPPublicKey;
-import org.bouncycastle.openpgp.PGPPublicKeyRing;
-import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
-import org.bouncycastle.openpgp.PGPUtil;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.util.NB;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.TimeZone;
-
-/** Encrypts {@link ContactInformation} instances and saves them. */
-@Singleton
-class EncryptedContactStore implements ContactStore {
- private static final Logger log =
- LoggerFactory.getLogger(EncryptedContactStore.class);
- private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
-
- private final SchemaFactory<ReviewDb> schema;
- private final PGPPublicKey dest;
- private final SecureRandom prng;
- private final URL storeUrl;
- private final String storeAPPSEC;
-
- EncryptedContactStore(final URL storeUrl, final String storeAPPSEC,
- final File pubKey, final SchemaFactory<ReviewDb> schema) {
- this.storeUrl = storeUrl;
- this.storeAPPSEC = storeAPPSEC;
- this.schema = schema;
- this.dest = selectKey(readPubRing(pubKey));
-
- final String prngName = "SHA1PRNG";
- try {
- prng = SecureRandom.getInstance(prngName);
- } catch (NoSuchAlgorithmException e) {
- throw new ProvisionException("Cannot create " + prngName, e);
- }
-
- // Run a test encryption to verify the proper algorithms exist in
- // our JVM and we are able to invoke them. This helps to identify
- // a system configuration problem early at startup, rather than a
- // lot later during runtime.
- //
- try {
- encrypt("test", new Date(0), "test".getBytes("UTF-8"));
- } catch (NoSuchProviderException e) {
- throw new ProvisionException("PGP encryption not available", e);
- } catch (PGPException e) {
- throw new ProvisionException("PGP encryption not available", e);
- } catch (IOException e) {
- throw new ProvisionException("PGP encryption not available", e);
- }
- }
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- private static PGPPublicKeyRingCollection readPubRing(final File pub) {
- try {
- InputStream in = new FileInputStream(pub);
- try {
- in = PGPUtil.getDecoderStream(in);
- return new PGPPublicKeyRingCollection(in);
- } finally {
- in.close();
- }
- } catch (IOException e) {
- throw new ProvisionException("Cannot read " + pub, e);
- } catch (PGPException e) {
- throw new ProvisionException("Cannot read " + pub, e);
- }
- }
-
- private static PGPPublicKey selectKey(final PGPPublicKeyRingCollection rings) {
- for (final Iterator<?> ri = rings.getKeyRings(); ri.hasNext();) {
- final PGPPublicKeyRing currRing = (PGPPublicKeyRing) ri.next();
- for (final Iterator<?> ki = currRing.getPublicKeys(); ki.hasNext();) {
- final PGPPublicKey k = (PGPPublicKey) ki.next();
- if (k.isEncryptionKey()) {
- return k;
- }
- }
- }
- return null;
- }
-
- public void store(final Account account, final ContactInformation info)
- throws ContactInformationStoreException {
- try {
- final byte[] plainText = format(account, info).getBytes("UTF-8");
- final String fileName = "account-" + account.getId();
- final Date fileDate = account.getContactFiledOn();
- final byte[] encText = encrypt(fileName, fileDate, plainText);
- final String encStr = new String(encText, "UTF-8");
-
- final Timestamp filedOn = account.getContactFiledOn();
- final UrlEncoded u = new UrlEncoded();
- if (storeAPPSEC != null) {
- u.put("APPSEC", storeAPPSEC);
- }
- if (account.getPreferredEmail() != null) {
- u.put("email", account.getPreferredEmail());
- }
- if (filedOn != null) {
- u.put("filed", String.valueOf(filedOn.getTime() / 1000L));
- }
- u.put("account_id", String.valueOf(account.getId().get()));
- u.put("data", encStr);
- final byte[] body = u.toString().getBytes("UTF-8");
-
- final HttpURLConnection c = (HttpURLConnection) storeUrl.openConnection();
- c.setRequestMethod("POST");
- c.setRequestProperty("Content-Type",
- "application/x-www-form-urlencoded; charset=UTF-8");
- c.setDoOutput(true);
- c.setFixedLengthStreamingMode(body.length);
- final OutputStream out = c.getOutputStream();
- out.write(body);
- out.close();
-
- if (c.getResponseCode() == 200) {
- final byte[] dst = new byte[2];
- final InputStream in = c.getInputStream();
- try {
- NB.readFully(in, dst, 0, 2);
- } finally {
- in.close();
- }
- if (dst[0] != 'O' || dst[1] != 'K') {
- throw new IOException("Store failed: " + c.getResponseCode());
- }
- } else {
- throw new IOException("Store failed: " + c.getResponseCode());
- }
-
- } catch (IOException e) {
- log.error("Cannot store encrypted contact information", e);
- throw new ContactInformationStoreException(e);
- } catch (PGPException e) {
- log.error("Cannot store encrypted contact information", e);
- throw new ContactInformationStoreException(e);
- } catch (NoSuchProviderException e) {
- log.error("Cannot store encrypted contact information", e);
- throw new ContactInformationStoreException(e);
- }
- }
-
- private byte[] encrypt(final String name, final Date date,
- final byte[] rawText) throws NoSuchProviderException, PGPException,
- IOException {
- final byte[] zText = compress(name, date, rawText);
- final PGPEncryptedDataGenerator cpk =
- new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true, prng, "BC");
- cpk.addMethod(dest);
-
- final ByteArrayOutputStream buf = new ByteArrayOutputStream();
- final ArmoredOutputStream aout = new ArmoredOutputStream(buf);
- final OutputStream cout = cpk.open(aout, zText.length);
- cout.write(zText);
- cout.close();
- aout.close();
-
- return buf.toByteArray();
- }
-
- private static byte[] compress(final String fileName, Date fileDate,
- final byte[] plainText) throws IOException {
- final ByteArrayOutputStream buf = new ByteArrayOutputStream();
- final PGPCompressedDataGenerator comdg;
- final int len = plainText.length;
- if (fileDate == null) {
- fileDate = PGPLiteralData.NOW;
- }
-
- comdg = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
- final OutputStream out =
- new PGPLiteralDataGenerator().open(comdg.open(buf),
- PGPLiteralData.BINARY, fileName, len, fileDate);
- out.write(plainText);
- out.close();
- comdg.close();
- return buf.toByteArray();
- }
-
- private String format(final Account account, final ContactInformation info)
- throws ContactInformationStoreException {
- Timestamp on = account.getContactFiledOn();
- if (on == null) {
- on = new Timestamp(System.currentTimeMillis());
- }
-
- final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- df.setTimeZone(UTC);
-
- final StringBuilder b = new StringBuilder();
- field(b, "Account-Id", account.getId().toString());
- field(b, "Date", df.format(on) + " " + UTC.getID());
- field(b, "Full-Name", account.getFullName());
- field(b, "Preferred-Email", account.getPreferredEmail());
-
- try {
- final ReviewDb db = schema.open();
- try {
- for (final AccountExternalId e : db.accountExternalIds().byAccount(
- account.getId())) {
- final StringBuilder oistr = new StringBuilder();
- if (e.getEmailAddress() != null && e.getEmailAddress().length() > 0) {
- if (oistr.length() > 0) {
- oistr.append(' ');
- }
- oistr.append(e.getEmailAddress());
- }
- if (e.isScheme(AccountExternalId.SCHEME_MAILTO)) {
- if (oistr.length() > 0) {
- oistr.append(' ');
- }
- oistr.append('<');
- oistr.append(e.getExternalId());
- oistr.append('>');
- }
- field(b, "Identity", oistr.toString());
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- throw new ContactInformationStoreException(e);
- }
-
- field(b, "Address", info.getAddress());
- field(b, "Country", info.getCountry());
- field(b, "Phone-Number", info.getPhoneNumber());
- field(b, "Fax-Number", info.getFaxNumber());
- return b.toString();
- }
-
- private static void field(final StringBuilder b, final String name,
- String value) {
- if (value == null) {
- return;
- }
- value = value.trim();
- if (value.length() == 0) {
- return;
- }
-
- b.append(name);
- b.append(':');
- if (value.indexOf('\n') == -1) {
- b.append(' ');
- b.append(value);
- } else {
- value = value.replaceAll("\r\n", "\n");
- value = value.replaceAll("\n", "\n\t");
- b.append("\n\t");
- b.append(value);
- }
- b.append('\n');
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/contact/NoContactStore.java b/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
deleted file mode 100644
index 1cd2f0822a..0000000000
--- a/src/main/java/com/google/gerrit/server/contact/NoContactStore.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.contact;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.rpc.ContactInformationStoreException;
-
-class NoContactStore implements ContactStore {
- @Override
- public boolean isEnabled() {
- return false;
- }
-
- @Override
- public void store(Account account, ContactInformation info)
- throws ContactInformationStoreException {
- throw new ContactInformationStoreException(new IllegalStateException(
- "ContactStore not configured"));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/BecomeAnyAccountLoginServlet.java b/src/main/java/com/google/gerrit/server/http/BecomeAnyAccountLoginServlet.java
deleted file mode 100644
index 6ef5e01f06..0000000000
--- a/src/main/java/com/google/gerrit/server/http/BecomeAnyAccountLoginServlet.java
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Writer;
-import java.util.Collections;
-import java.util.List;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@SuppressWarnings("serial")
-@Singleton
-public class BecomeAnyAccountLoginServlet extends HttpServlet {
- private final SchemaFactory<ReviewDb> schema;
- private final Provider<WebSession> webSession;
- private final Provider<String> urlProvider;
- private final byte[] raw;
-
- @Inject
- BecomeAnyAccountLoginServlet(final Provider<WebSession> ws,
- final SchemaFactory<ReviewDb> sf,
- final @CanonicalWebUrl @Nullable Provider<String> up,
- final ServletContext servletContext) throws IOException {
- webSession = ws;
- schema = sf;
- urlProvider = up;
-
- final String hostPageName = "WEB-INF/BecomeAnyAccount.html";
- final String doc = HtmlDomUtil.readFile(servletContext, "/" + hostPageName);
- if (doc == null) {
- throw new FileNotFoundException("No " + hostPageName + " in webapp");
- }
-
- raw = doc.getBytes(HtmlDomUtil.ENC);
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- doPost(req, rsp);
- }
-
- @Override
- protected void doPost(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
-
- final List<Account> accounts;
- if (req.getParameter("ssh_user_name") != null) {
- accounts = bySshUserName(rsp, req.getParameter("ssh_user_name"));
-
- } else if (req.getParameter("preferred_email") != null) {
- accounts = byPreferredEmail(rsp, req.getParameter("preferred_email"));
-
- } else if (req.getParameter("account_id") != null) {
- accounts = byAccountId(rsp, req.getParameter("account_id"));
-
- } else {
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC);
- rsp.setContentLength(raw.length);
- final OutputStream out = rsp.getOutputStream();
- try {
- out.write(raw);
- } finally {
- out.close();
- }
- return;
- }
-
- if (accounts.size() == 1) {
- final Account account = accounts.get(0);
- webSession.get().login(account.getId(), false);
- rsp.sendRedirect(urlProvider.get());
-
- } else {
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC);
- final Writer out = rsp.getWriter();
- out.write("<html>");
- out.write("<body>");
- out.write("<h1>Account Not Found</h1>");
- out.write("</body>");
- out.write("</html>");
- out.close();
- }
- }
-
- private List<Account> bySshUserName(final HttpServletResponse rsp,
- final String userName) {
- try {
- final ReviewDb db = schema.open();
- try {
- final Account account = db.accounts().bySshUserName(userName);
- return account != null ? Collections.<Account> singletonList(account)
- : Collections.<Account> emptyList();
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- getServletContext().log("cannot query database", e);
- return Collections.<Account> emptyList();
- }
- }
-
- private List<Account> byPreferredEmail(final HttpServletResponse rsp,
- final String email) {
- try {
- final ReviewDb db = schema.open();
- try {
- return db.accounts().byPreferredEmail(email).toList();
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- getServletContext().log("cannot query database", e);
- return Collections.<Account> emptyList();
- }
- }
-
- private List<Account> byAccountId(final HttpServletResponse rsp,
- final String idStr) {
- final Account.Id id;
- try {
- id = Account.Id.parse(idStr);
- } catch (NumberFormatException nfe) {
- return Collections.<Account> emptyList();
- }
- try {
- final ReviewDb db = schema.open();
- try {
- final Account account = db.accounts().get(id);
- return account != null ? Collections.<Account> singletonList(account)
- : Collections.<Account> emptyList();
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- getServletContext().log("cannot query database", e);
- return Collections.<Account> emptyList();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/CatServlet.java b/src/main/java/com/google/gerrit/server/http/CatServlet.java
deleted file mode 100644
index 01b3590ccb..0000000000
--- a/src/main/java/com/google/gerrit/server/http/CatServlet.java
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.FileTypeRegistry;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import eu.medsea.mimeutil.MimeType;
-
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.TreeWalk;
-import org.eclipse.jgit.util.NB;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Exports a single version of a patch as a normal file download.
- * <p>
- * This can be relatively unsafe with Microsoft Internet Explorer 6.0 as the
- * browser will (rather incorrectly) treat an HTML or JavaScript file its
- * supposed to download as though it was served by this site, and will execute
- * it with the site's own protection domain. This opens a massive security hole
- * so we package the content into a zip file.
- */
-@SuppressWarnings("serial")
-@Singleton
-public class CatServlet extends HttpServlet {
- private static final MimeType ZIP = new MimeType("application/zip");
- private final Provider<ReviewDb> requestDb;
- private final GitRepositoryManager repoManager;
- private final SecureRandom rng;
- private final FileTypeRegistry registry;
- private final ChangeControl.Factory changeControl;
-
- @Inject
- CatServlet(final GitRepositoryManager grm, final Provider<ReviewDb> sf,
- final FileTypeRegistry ftr, final ChangeControl.Factory ccf) {
- requestDb = sf;
- repoManager = grm;
- rng = new SecureRandom();
- registry = ftr;
- changeControl = ccf;
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- String keyStr = req.getPathInfo();
-
- // We shouldn't have to do this extra decode pass, but somehow we
- // are now receiving our "^1" suffix as "%5E1", which confuses us
- // downstream. Other times we get our embedded "," as "%2C", which
- // is equally bad. And yet when these happen a "%2F" is left as-is,
- // rather than escaped as "%252F", which makes me feel really really
- // uncomfortable with a blind decode right here.
- //
- keyStr = URLDecoder.decode(keyStr, "UTF-8");
-
- if (!keyStr.startsWith("/")) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- keyStr = keyStr.substring(1);
-
- final Patch.Key patchKey;
- final int side;
- {
- final int c = keyStr.lastIndexOf('^');
- if (c == 0) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- if (c < 0) {
- side = 0;
-
- } else {
- try {
- side = Integer.parseInt(keyStr.substring(c + 1));
- keyStr = keyStr.substring(0, c);
- } catch (NumberFormatException e) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- }
-
- try {
- patchKey = Patch.Key.parse(keyStr);
- } catch (NumberFormatException e) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- }
-
- final Change.Id changeId = patchKey.getParentKey().getParentKey();
- final Project project;
- final PatchSet patchSet;
- try {
- final ReviewDb db = requestDb.get();
- final ChangeControl control = changeControl.validateFor(changeId);
-
- project = control.getProject();
- patchSet = db.patchSets().get(patchKey.getParentKey());
- if (patchSet == null) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- } catch (NoSuchChangeException e) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- } catch (OrmException e) {
- getServletContext().log("Cannot query database", e);
- rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- final Repository repo;
- try {
- repo = repoManager.openRepository(project.getNameKey().get());
- } catch (RepositoryNotFoundException e) {
- getServletContext().log("Cannot open repository", e);
- rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
-
- final byte[] blobData;
- final RevCommit fromCommit;
- final String suffix;
- final String path = patchKey.getFileName();
- try {
- final RevWalk rw = new RevWalk(repo);
- final RevCommit c;
- final TreeWalk tw;
-
- c = rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
- if (side == 0) {
- fromCommit = c;
- suffix = "new";
-
- } else if (1 <= side && side - 1 < c.getParentCount()) {
- fromCommit = rw.parseCommit(c.getParent(side - 1));
- if (c.getParentCount() == 1) {
- suffix = "old";
- } else {
- suffix = "old" + side;
- }
-
- } else {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- tw = TreeWalk.forPath(repo, path, fromCommit.getTree());
- if (tw == null) {
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- if (tw.getFileMode(0).getObjectType() == Constants.OBJ_BLOB) {
- blobData = repo.openBlob(tw.getObjectId(0)).getCachedBytes();
-
- } else {
- rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- }
- } catch (IOException e) {
- getServletContext().log("Cannot read repository", e);
- rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- } catch (RuntimeException e) {
- getServletContext().log("Cannot read repository", e);
- rsp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- return;
- } finally {
- repo.close();
- }
-
- final long when = fromCommit.getCommitTime() * 1000L;
- MimeType contentType = registry.getMimeType(path, blobData);
- final byte[] outData;
-
- if (registry.isSafeInline(contentType)) {
- outData = blobData;
-
- } else {
- // The content may not be safe to transmit inline, as a browser might
- // interpret it as HTML or JavaScript hosted by this site. Such code
- // might then run in the site's security domain, and may be able to use
- // the user's cookies to perform unauthorized actions.
- //
- // Usually, wrapping the content into a ZIP file forces the browser to
- // save the content to the local system instead.
- //
- final ByteArrayOutputStream zip = new ByteArrayOutputStream();
- final ZipOutputStream zo = new ZipOutputStream(zip);
- final ZipEntry e = new ZipEntry(safeFileName(path, rand(req, suffix)));
- e.setComment(fromCommit.name() + ":" + path);
- e.setSize(blobData.length);
- e.setTime(when);
- zo.putNextEntry(e);
- zo.write(blobData);
- zo.closeEntry();
- zo.close();
-
- outData = zip.toByteArray();
- contentType = ZIP;
-
- rsp.setHeader("Content-Disposition", "attachment; filename=\""
- + safeFileName(path, suffix) + ".zip" + "\"");
- }
-
- rsp.setContentType(contentType.toString());
- rsp.setContentLength(outData.length);
- rsp.setDateHeader("Last-Modified", when);
- rsp.setDateHeader("Expires", 0L);
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.getOutputStream().write(outData);
- }
-
- private static String safeFileName(String fileName, final String suffix) {
- // Convert a file path (e.g. "src/Init.c") to a safe file name with
- // no meta-characters that might be unsafe on any given platform.
- //
- final int slash = fileName.lastIndexOf('/');
- if (slash >= 0) {
- fileName = fileName.substring(slash + 1);
- }
-
- final StringBuilder r = new StringBuilder(fileName.length());
- for (int i = 0; i < fileName.length(); i++) {
- final char c = fileName.charAt(i);
- if (c == '_' || c == '-' || c == '.' || c == '@') {
- r.append(c);
-
- } else if ('0' <= c && c <= '9') {
- r.append(c);
-
- } else if ('A' <= c && c <= 'Z') {
- r.append(c);
-
- } else if ('a' <= c && c <= 'z') {
- r.append(c);
-
- } else if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
- r.append('-');
-
- } else {
- r.append('_');
- }
- }
- fileName = r.toString();
-
- final int ext = fileName.lastIndexOf('.');
- if (ext <= 0) {
- return fileName + "_" + suffix;
-
- } else {
- return fileName.substring(0, ext) + "_" + suffix
- + fileName.substring(ext);
- }
- }
-
- private String rand(final HttpServletRequest req, final String suffix)
- throws UnsupportedEncodingException {
- // Produce a random suffix that is difficult (or nearly impossible)
- // for an attacker to guess in advance. This reduces the risk that
- // an attacker could upload a *.class file and have us send a ZIP
- // that can be invoked through an applet tag in the victim's browser.
- //
- final MessageDigest md = Constants.newMessageDigest();
- final byte[] buf = new byte[8];
-
- NB.encodeInt32(buf, 0, req.getRemotePort());
- md.update(req.getRemoteAddr().getBytes("UTF-8"));
- md.update(buf, 0, 4);
-
- NB.encodeInt64(buf, 0, System.currentTimeMillis());
- md.update(buf, 0, 8);
-
- rng.nextBytes(buf);
- md.update(buf, 0, 8);
-
- return suffix + "-" + ObjectId.fromRaw(md.digest()).name();
- }
-
-}
diff --git a/src/main/java/com/google/gerrit/server/http/CookieBase64.java b/src/main/java/com/google/gerrit/server/http/CookieBase64.java
deleted file mode 100644
index f0092d3ac7..0000000000
--- a/src/main/java/com/google/gerrit/server/http/CookieBase64.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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.
-//
-// This code is based heavily on Robert Harder's <rob@iharder.net>
-// public domain Base64 class, version 2.1.
-//
-
-package com.google.gerrit.server.http;
-
-/** Base64 encoder which uses a language safe within HTTP cookies. */
-class CookieBase64 {
- private static final char[] enc;
-
- static {
- enc = new char[64];
- int o = 0;
- o = fill(enc, o, 'a', 'z');
- o = fill(enc, o, 'A', 'Z');
- o = fill(enc, o, '0', '9');
- enc[o++] = '-';
- enc[o++] = '.';
- }
-
- private static int fill(final char[] out, int o, final char f, final int l) {
- for (char c = f; c <= l; c++)
- out[o++] = c;
- return o;
- }
-
- static String encode(final byte[] in) {
- final StringBuilder out = new StringBuilder(in.length * 4 / 3);
- final int len2 = in.length - 2;
- int d = 0;
- for (; d < len2; d += 3) {
- encode3to4(out, in, d, 3);
- }
- if (d < in.length) {
- encode3to4(out, in, d, in.length - d);
- }
- return out.toString();
- }
-
- private static void encode3to4(final StringBuilder out, final byte[] in,
- final int inOffset, final int numSigBytes) {
- // 1 2 3
- // 01234567890123456789012345678901 Bit position
- // --------000000001111111122222222 Array position from threeBytes
- // --------| || || || | Six bit groups to index ALPHABET
- // >>18 >>12 >> 6 >> 0 Right shift necessary
- // 0x3f 0x3f 0x3f Additional AND
-
- // Create buffer with zero-padding if there are only one or two
- // significant bytes passed in the array.
- // We have to shift left 24 in order to flush out the 1's that appear
- // when Java treats a value as negative that is cast from a byte to an int.
- //
- int inBuff = ( numSigBytes > 0 ? ((in[ inOffset ] << 24) >>> 8) : 0 )
- | ( numSigBytes > 1 ? ((in[ inOffset + 1 ] << 24) >>> 16) : 0 )
- | ( numSigBytes > 2 ? ((in[ inOffset + 2 ] << 24) >>> 24) : 0 );
-
- switch (numSigBytes) {
- case 3:
- out.append(enc[(inBuff >>> 18)]);
- out.append(enc[(inBuff >>> 12) & 0x3f]);
- out.append(enc[(inBuff >>> 6) & 0x3f]);
- out.append(enc[(inBuff) & 0x3f]);
- break;
-
- case 2:
- out.append(enc[(inBuff >>> 18)]);
- out.append(enc[(inBuff >>> 12) & 0x3f]);
- out.append(enc[(inBuff >>> 6) & 0x3f]);
- break;
-
- case 1:
- out.append(enc[(inBuff >>> 18)]);
- out.append(enc[(inBuff >>> 12) & 0x3f]);
- break;
-
- default:
- break;
- }
- }
-
- private CookieBase64() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/GerritConfigProvider.java b/src/main/java/com/google/gerrit/server/http/GerritConfigProvider.java
deleted file mode 100644
index 0ee04d5e85..0000000000
--- a/src/main/java/com/google/gerrit/server/http/GerritConfigProvider.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.client.data.GitwebLink;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.gerrit.server.contact.ContactStore;
-import com.google.gerrit.server.mail.EmailSender;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.gwtexpui.safehtml.client.RegexFindReplace;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.lib.Config;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-public class GerritConfigProvider implements Provider<GerritConfig> {
- private final Realm realm;
- private final Config cfg;
- private final String canonicalWebUrl;
- private final AuthConfig authConfig;
- private final Project.NameKey wildProject;
- private final SshInfo sshInfo;
- private final ApprovalTypes approvalTypes;
-
- private EmailSender emailSender;
- private final ContactStore contactStore;
-
- @Inject
- GerritConfigProvider(final Realm r, @GerritServerConfig final Config gsc,
- @CanonicalWebUrl @Nullable final String cwu, final AuthConfig ac,
- @WildProjectName final Project.NameKey wp, final SshInfo si,
- final ApprovalTypes at, final ContactStore cs) {
- realm = r;
- cfg = gsc;
- canonicalWebUrl = cwu;
- authConfig = ac;
- sshInfo = si;
- wildProject = wp;
- approvalTypes = at;
- contactStore = cs;
- }
-
- @Inject(optional = true)
- void setEmailSender(final EmailSender d) {
- emailSender = d;
- }
-
- private GerritConfig create() {
- final GerritConfig config = new GerritConfig();
- config.setCanonicalUrl(canonicalWebUrl);
- config.setUseContributorAgreements(cfg.getBoolean("auth",
- "contributoragreements", false));
- config.setGitDaemonUrl(cfg.getString("gerrit", null, "canonicalgiturl"));
- config.setUseRepoDownload(cfg.getBoolean("repo", null,
- "showdownloadcommand", false));
- config.setUseContactInfo(contactStore != null && contactStore.isEnabled());
- config.setAuthType(authConfig.getAuthType());
- config.setWildProject(wildProject);
- config.setApprovalTypes(approvalTypes);
-
- final Set<Account.FieldName> fields = new HashSet<Account.FieldName>();
- for (final Account.FieldName n : Account.FieldName.values()) {
- if (realm.allowsEdit(n)) {
- fields.add(n);
- }
- }
- if (emailSender != null && emailSender.isEnabled()) {
- fields.add(Account.FieldName.REGISTER_NEW_EMAIL);
- }
- config.setEditableAccountFields(fields);
-
- final String gitwebUrl = cfg.getString("gitweb", null, "url");
- if (gitwebUrl != null) {
- config.setGitwebLink(new GitwebLink(gitwebUrl));
- }
-
- config.setSshdAddress(sshInfo != null ? sshInfo.getSshdAddress() : null);
-
- ArrayList<String> commentLinkNames =
- new ArrayList<String>(cfg.getSubsections("CommentLink"));
- ArrayList<RegexFindReplace> commentLinks =
- new ArrayList<RegexFindReplace>(commentLinkNames.size());
- for (String commentLinkName : commentLinkNames) {
- String match = cfg.getString("commentlink", commentLinkName, "match");
- String link =
- "<a href=\"" + cfg.getString("commentlink", commentLinkName, "link")
- + "\">$&</a>";
- commentLinks.add(new RegexFindReplace(match, link));
- }
- config.setCommentLinks(commentLinks);
-
- return config;
- }
-
- @Override
- public GerritConfig get() {
- return create();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/GerritJsonServlet.java b/src/main/java/com/google/gerrit/server/http/GerritJsonServlet.java
deleted file mode 100644
index c61bdf5715..0000000000
--- a/src/main/java/com/google/gerrit/server/http/GerritJsonServlet.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.rpc.NotSignedInException;
-import com.google.gerrit.client.rpc.SignInRequired;
-import com.google.gson.GsonBuilder;
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.gwtjsonrpc.server.ActiveCall;
-import com.google.gwtjsonrpc.server.JsonServlet;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Base JSON servlet to ensure the current user is not forged.
- */
-@SuppressWarnings("serial")
-final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall> {
- private final Provider<WebSession> session;
- private final RemoteJsonService service;
-
- @Inject
- GerritJsonServlet(final Provider<WebSession> w, final RemoteJsonService s) {
- session = w;
- service = s;
- }
-
- @Override
- protected GerritCall createActiveCall(final HttpServletRequest req,
- final HttpServletResponse rsp) {
- return new GerritCall(session.get(), req, rsp);
- }
-
- @Override
- protected GsonBuilder createGsonBuilder() {
- final GsonBuilder g = super.createGsonBuilder();
-
- g.registerTypeAdapter(org.eclipse.jgit.diff.Edit.class,
- new org.eclipse.jgit.diff.EditDeserializer());
-
- return g;
- }
-
- @Override
- protected void preInvoke(final GerritCall call) {
- super.preInvoke(call);
-
- if (call.isComplete()) {
- return;
- }
-
- if (call.getMethod().getAnnotation(SignInRequired.class) != null) {
- // If SignInRequired is set on this method we must have both a
- // valid XSRF token *and* have the user signed in. Doing these
- // checks also validates that they agree on the user identity.
- //
- if (!call.requireXsrfValid() || !session.get().isSignedIn()) {
- call.onFailure(new NotSignedInException());
- return;
- }
- }
- }
-
- @Override
- protected Object createServiceHandle() {
- return service;
- }
-
- static class GerritCall extends ActiveCall {
- private final WebSession session;
-
- GerritCall(final WebSession session, final HttpServletRequest i,
- final HttpServletResponse o) {
- super(i, o);
- this.session = session;
- }
-
- @Override
- public void onFailure(final Throwable error) {
- if (error instanceof IllegalArgumentException
- || error instanceof IllegalStateException) {
- super.onFailure(error);
- } else if (error instanceof OrmException
- || error instanceof RuntimeException) {
- onInternalFailure(error);
- } else {
- super.onFailure(error);
- }
- }
-
- @Override
- public boolean xsrfValidate() {
- final String keyIn = getXsrfKeyIn();
- if (keyIn == null || "".equals(keyIn)) {
- // Anonymous requests don't need XSRF protection, they shouldn't
- // be able to cause critical state changes.
- //
- return !session.isSignedIn();
-
- } else {
- // The session must exist, and must be using this token.
- //
- return session.isSignedIn() && session.isTokenValid(keyIn);
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/GerritJsonServletProvider.java b/src/main/java/com/google/gerrit/server/http/GerritJsonServletProvider.java
deleted file mode 100644
index 3709403653..0000000000
--- a/src/main/java/com/google/gerrit/server/http/GerritJsonServletProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-
-/** Creates {@link GerritJsonServlet} with a {@link RemoteJsonService}. */
-class GerritJsonServletProvider implements Provider<GerritJsonServlet> {
- @Inject
- private Injector injector;
-
- private final Class<? extends RemoteJsonService> serviceClass;
-
- @Inject
- GerritJsonServletProvider(final Class<? extends RemoteJsonService> c) {
- serviceClass = c;
- }
-
- @Override
- public GerritJsonServlet get() {
- final RemoteJsonService srv = injector.getInstance(serviceClass);
- return injector.createChildInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(RemoteJsonService.class).toInstance(srv);
- }
- }).getInstance(GerritJsonServlet.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/GerritServletConfig.java b/src/main/java/com/google/gerrit/server/http/GerritServletConfig.java
deleted file mode 100644
index df21460523..0000000000
--- a/src/main/java/com/google/gerrit/server/http/GerritServletConfig.java
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import static com.google.inject.Stage.PRODUCTION;
-
-import com.google.gerrit.git.PushAllProjectsOp;
-import com.google.gerrit.git.ReloadSubmitQueueOp;
-import com.google.gerrit.git.WorkQueue;
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.config.CanonicalWebUrlModule;
-import com.google.gerrit.server.config.DatabaseModule;
-import com.google.gerrit.server.config.GerritGlobalModule;
-import com.google.gerrit.server.ssh.SshDaemon;
-import com.google.gerrit.server.ssh.SshModule;
-import com.google.gerrit.server.ssh.commands.MasterCommandModule;
-import com.google.inject.ConfigurationException;
-import com.google.inject.CreationException;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.GuiceServletContextListener;
-import com.google.inject.spi.Message;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.http.HttpServletRequest;
-import javax.sql.DataSource;
-
-/** Configures the web application environment for Gerrit Code Review. */
-public class GerritServletConfig extends GuiceServletContextListener {
- private static final Logger log =
- LoggerFactory.getLogger(GerritServletConfig.class);
-
- private Injector dbInjector;
- private Injector sysInjector;
- private Injector webInjector;
- private Injector sshInjector;
-
- private synchronized void init() {
- if (sysInjector == null) {
- try {
- dbInjector = Guice.createInjector(PRODUCTION, new DatabaseModule());
- } catch (CreationException ce) {
- final Message first = ce.getErrorMessages().iterator().next();
- final StringBuilder buf = new StringBuilder();
- buf.append(first.getMessage());
- Throwable why = first.getCause();
- while (why != null) {
- buf.append("\n caused by ");
- buf.append(why.toString());
- why = why.getCause();
- }
- if (first.getCause() != null) {
- buf.append("\n");
- buf.append("\nResolve above errors before continuing.");
- buf.append("\nComplete stack trace follows:");
- }
- log.error(buf.toString(), first.getCause());
- throw new CreationException(Collections.singleton(first));
- }
-
- sysInjector =
- GerritGlobalModule.createInjector(dbInjector,
- new CanonicalWebUrlModule() {
- @Override
- protected Class<? extends Provider<String>> provider() {
- return HttpCanonicalWebUrlProvider.class;
- }
- });
- sshInjector = createSshInjector();
- webInjector = createWebInjector();
-
- // Push the Provider<HttpServletRequest> down into the canonical
- // URL provider. Its optional for that provider, but since we can
- // supply one we should do so, in case the administrator has not
- // setup the canonical URL in the configuration file.
- //
- // Note we have to do this manually as Guice failed to do the
- // injection here because the HTTP environment is not visible
- // to the core server modules.
- //
- sysInjector.getInstance(HttpCanonicalWebUrlProvider.class)
- .setHttpServletRequest(
- webInjector.getProvider(HttpServletRequest.class));
- }
- }
-
- private Injector createSshInjector() {
- return sysInjector.createChildInjector(new SshModule(),
- new MasterCommandModule());
- }
-
- private Injector createWebInjector() {
- final List<Module> modules = new ArrayList<Module>();
- modules.add(sshInjector.getInstance(WebModule.class));
- return sysInjector.createChildInjector(modules);
- }
-
- @Override
- protected Injector getInjector() {
- init();
- return webInjector;
- }
-
- @Override
- public void contextInitialized(final ServletContextEvent event) {
- super.contextInitialized(event);
- init();
-
- try {
- sysInjector.getInstance(CachePool.class).start();
- } catch (ConfigurationException e) {
- log.error("Unable to start CachePool", e);
- } catch (ProvisionException e) {
- log.error("Unable to start CachePool", e);
- }
-
- try {
- sysInjector.getInstance(PushAllProjectsOp.Factory.class).create(null)
- .start(30, TimeUnit.SECONDS);
- } catch (ConfigurationException e) {
- log.error("Unable to restart replication queue", e);
- } catch (ProvisionException e) {
- log.error("Unable to restart replication queue", e);
- }
-
- try {
- sysInjector.getInstance(ReloadSubmitQueueOp.Factory.class).create()
- .start(15, TimeUnit.SECONDS);
- } catch (ConfigurationException e) {
- log.error("Unable to restart merge queue", e);
- } catch (ProvisionException e) {
- log.error("Unable to restart merge queue", e);
- }
-
- try {
- sshInjector.getInstance(SshDaemon.class).start();
- } catch (ConfigurationException e) {
- log.error("Unable to start SSHD", e);
- } catch (ProvisionException e) {
- log.error("Unable to start SSHD", e);
- } catch (IOException e) {
- log.error("Unable to start SSHD", e);
- }
- }
-
- @Override
- public void contextDestroyed(final ServletContextEvent event) {
- try {
- if (sshInjector != null) {
- sshInjector.getInstance(SshDaemon.class).stop();
- }
- } catch (ConfigurationException e) {
- } catch (ProvisionException e) {
- }
-
- try {
- if (sysInjector != null) {
- sysInjector.getInstance(WorkQueue.class).shutdown();
- }
- } catch (ConfigurationException e) {
- } catch (ProvisionException e) {
- }
-
- try {
- if (sysInjector != null) {
- sysInjector.getInstance(CachePool.class).stop();
- }
- } catch (ConfigurationException e) {
- } catch (ProvisionException e) {
- }
-
- try {
- if (dbInjector != null) {
- closeDataSource(dbInjector.getInstance(DatabaseModule.DS));
- }
- } catch (ConfigurationException ce) {
- } catch (ProvisionException ce) {
- }
-
- super.contextDestroyed(event);
- }
-
- private void closeDataSource(final DataSource ds) {
- try {
- Class<?> type = Class.forName("org.apache.commons.dbcp.BasicDataSource");
- if (type.isInstance(ds)) {
- type.getMethod("close").invoke(ds);
- return;
- }
- } catch (Throwable bad) {
- // Oh well, its not a Commons DBCP pooled connection.
- }
-
- try {
- Class<?> type = Class.forName("com.mchange.v2.c3p0.DataSources");
- if (type.isInstance(ds)) {
- type.getMethod("destroy", DataSource.class).invoke(null, ds);
- return;
- }
- } catch (Throwable bad) {
- // Oh well, its not a c3p0 pooled connection.
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HostPageServlet.java b/src/main/java/com/google/gerrit/server/http/HostPageServlet.java
deleted file mode 100644
index e8149b655d..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HostPageServlet.java
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.HostPageData;
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.config.SitePath;
-import com.google.gwt.user.server.rpc.RPCServletUtils;
-import com.google.gwtjsonrpc.server.JsonServlet;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.security.MessageDigest;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/** Sends the Gerrit host page to clients. */
-@SuppressWarnings("serial")
-@Singleton
-public class HostPageServlet extends HttpServlet {
- private final Provider<CurrentUser> currentUser;
- private final File sitePath;
- private final GerritConfig config;
- private final Provider<String> urlProvider;
- private final boolean wantSSL;
- private final Document hostDoc;
-
- @Inject
- HostPageServlet(final Provider<CurrentUser> cu, @SitePath final File path,
- final GerritConfig gc,
- @CanonicalWebUrl @Nullable final Provider<String> up,
- @CanonicalWebUrl @Nullable final String configuredUrl,
- final ServletContext servletContext) throws IOException {
- currentUser = cu;
- urlProvider = up;
- sitePath = path;
- config = gc;
- wantSSL = configuredUrl != null && configuredUrl.startsWith("https:");
-
- final String hostPageName = "WEB-INF/HostPage.html";
- hostDoc = HtmlDomUtil.parseFile(servletContext, "/" + hostPageName);
- if (hostDoc == null) {
- throw new FileNotFoundException("No " + hostPageName + " in webapp");
- }
- fixModuleReference(hostDoc, servletContext);
- injectCssFile(hostDoc, "gerrit_sitecss", sitePath, "GerritSite.css");
- injectXmlFile(hostDoc, "gerrit_header", sitePath, "GerritSiteHeader.html");
- injectXmlFile(hostDoc, "gerrit_footer", sitePath, "GerritSiteFooter.html");
- }
-
- private void injectXmlFile(final Document hostDoc, final String id,
- final File sitePath, final String fileName) throws IOException {
- final Element banner = HtmlDomUtil.find(hostDoc, id);
- if (banner == null) {
- return;
- }
-
- while (banner.getFirstChild() != null) {
- banner.removeChild(banner.getFirstChild());
- }
-
- final Document html = HtmlDomUtil.parseFile(sitePath, fileName);
- if (html == null) {
- banner.getParentNode().removeChild(banner);
- return;
- }
-
- final Element content = html.getDocumentElement();
- banner.appendChild(hostDoc.importNode(content, true));
- }
-
- private void injectCssFile(final Document hostDoc, final String id,
- final File sitePath, final String fileName) throws IOException {
- final Element banner = HtmlDomUtil.find(hostDoc, id);
- if (banner == null) {
- return;
- }
-
- while (banner.getFirstChild() != null) {
- banner.removeChild(banner.getFirstChild());
- }
-
- final String css = HtmlDomUtil.readFile(sitePath, fileName);
- if (css == null) {
- banner.getParentNode().removeChild(banner);
- return;
- }
-
- banner.removeAttribute("id");
- banner.appendChild(hostDoc.createCDATASection("\n" + css + "\n"));
- }
-
- private void injectJson(final Document hostDoc, final String id,
- final Object obj) {
- final Element scriptNode = HtmlDomUtil.find(hostDoc, id);
- if (scriptNode == null) {
- return;
- }
-
- while (scriptNode.getFirstChild() != null) {
- scriptNode.removeChild(scriptNode.getFirstChild());
- }
-
- if (obj == null) {
- scriptNode.getParentNode().removeChild(scriptNode);
- return;
- }
-
- final StringWriter w = new StringWriter();
- w.write("<!--\n");
- w.write("var ");
- w.write(id);
- w.write("_obj=");
- JsonServlet.defaultGsonBuilder().create().toJson(obj, w);
- w.write(";\n// -->\n");
- scriptNode.removeAttribute("id");
- scriptNode.setAttribute("type", "text/javascript");
- scriptNode.setAttribute("language", "javascript");
- scriptNode.appendChild(hostDoc.createCDATASection(w.toString()));
- }
-
- private void fixModuleReference(final Document hostDoc,
- final ServletContext servletContext) throws IOException {
- final Element scriptNode = HtmlDomUtil.find(hostDoc, "gerrit_module");
- if (scriptNode == null) {
- throw new IOException("No gerrit_module to rewrite in host document");
- }
- scriptNode.removeAttribute("id");
-
- final String src = scriptNode.getAttribute("src");
- InputStream in = servletContext.getResourceAsStream("/" + src);
- if (in == null) {
- throw new IOException("No " + src + " in webapp root");
- }
-
- final MessageDigest md = Constants.newMessageDigest();
- try {
- try {
- final byte[] buf = new byte[1024];
- int n;
- while ((n = in.read(buf)) > 0) {
- md.update(buf, 0, n);
- }
- } finally {
- in.close();
- }
- } catch (IOException e) {
- throw new IOException("Failed reading " + src, e);
- }
-
- final String vstr = ObjectId.fromRaw(md.digest()).name();
- scriptNode.setAttribute("src", src + "?content=" + vstr);
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- // If we wanted SSL, but the user didn't come to us over an SSL channel,
- // force it to be SSL by issuing a protocol redirect. Try to keep the
- // name "localhost" in case this is an SSH port tunnel.
- //
- if (wantSSL && !isSecure(req)) {
- final StringBuffer reqUrl = req.getRequestURL();
- if (isLocalHost(req)) {
- reqUrl.replace(0, reqUrl.indexOf(":"), "https");
- } else {
- reqUrl.setLength(0);
- reqUrl.append(urlProvider.get());
- }
- rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
- rsp.setHeader("Location", reqUrl.toString());
- return;
- }
-
- final HostPageData pageData = new HostPageData();
- pageData.config = config;
-
- final CurrentUser user = currentUser.get();
- if (user instanceof IdentifiedUser) {
- pageData.userAccount = ((IdentifiedUser) user).getAccount();
- }
-
- final Document peruser = HtmlDomUtil.clone(hostDoc);
- injectJson(peruser, "gerrit_hostpagedata", pageData);
-
- final byte[] raw = HtmlDomUtil.toUTF8(peruser);
- final byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
- rsp.setHeader("Content-Encoding", "gzip");
- tosend = HtmlDomUtil.compress(raw);
- } else {
- tosend = raw;
- }
-
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC);
- rsp.setContentLength(tosend.length);
- final OutputStream out = rsp.getOutputStream();
- try {
- out.write(tosend);
- } finally {
- out.close();
- }
- }
-
- private static boolean isSecure(final HttpServletRequest req) {
- return "https".equals(req.getScheme()) || req.isSecure();
- }
-
- private static boolean isLocalHost(final HttpServletRequest req) {
- return "localhost".equals(req.getServerName())
- || "127.0.0.1".equals(req.getServerName());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HtmlDomUtil.java b/src/main/java/com/google/gerrit/server/http/HtmlDomUtil.java
deleted file mode 100644
index fddc4ea505..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HtmlDomUtil.java
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.zip.GZIPOutputStream;
-
-import javax.servlet.ServletContext;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerConfigurationException;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-/** Utility functions to deal with HTML using W3C DOM operations. */
-public class HtmlDomUtil {
- /** Standard character encoding we prefer (UTF-8). */
- public static final String ENC = "UTF-8";
-
- /** DOCTYPE for a standards mode HTML document. */
- public static final String HTML_STRICT =
- "-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd";
-
- /** Convert a document to a UTF-8 byte sequence. */
- public static byte[] toUTF8(final Document hostDoc) throws IOException {
- return toString(hostDoc).getBytes(ENC);
- }
-
- /** Compress the document. */
- public static byte[] compress(final byte[] raw) throws IOException {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- final GZIPOutputStream gz = new GZIPOutputStream(out);
- gz.write(raw);
- gz.finish();
- gz.flush();
- return out.toByteArray();
- }
-
- /** Convert a document to a String, assuming later encoding to UTF-8. */
- public static String toString(final Document hostDoc) throws IOException {
- try {
- final StringWriter out = new StringWriter();
- final DOMSource domSource = new DOMSource(hostDoc);
- final StreamResult streamResult = new StreamResult(out);
- final TransformerFactory tf = TransformerFactory.newInstance();
- final Transformer serializer = tf.newTransformer();
- serializer.setOutputProperty(OutputKeys.ENCODING, ENC);
- serializer.setOutputProperty(OutputKeys.METHOD, "html");
- serializer.setOutputProperty(OutputKeys.INDENT, "no");
- serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,
- HtmlDomUtil.HTML_STRICT);
- serializer.transform(domSource, streamResult);
- return out.toString();
- } catch (TransformerConfigurationException e) {
- final IOException r = new IOException("Error transforming page");
- r.initCause(e);
- throw r;
- } catch (TransformerException e) {
- final IOException r = new IOException("Error transforming page");
- r.initCause(e);
- throw r;
- }
- }
-
- /** Find an element by its "id" attribute; null if no element is found. */
- public static Element find(final Node parent, final String name) {
- final NodeList list = parent.getChildNodes();
- for (int i = 0; i < list.getLength(); i++) {
- final Node n = list.item(i);
- if (n instanceof Element) {
- final Element e = (Element) n;
- if (name.equals(e.getAttribute("id"))) {
- return e;
- }
- }
- final Element r = find(n, name);
- if (r != null) {
- return r;
- }
- }
- return null;
- }
-
- /** Append an HTML &lt;input type="hidden"&gt; to the form. */
- public static void addHidden(final Element form, final String name,
- final String value) {
- final Element in = form.getOwnerDocument().createElement("input");
- in.setAttribute("type", "hidden");
- in.setAttribute("name", name);
- in.setAttribute("value", value);
- form.appendChild(in);
- }
-
- /** Clone a document so it can be safely modified on a per-request basis. */
- public static Document clone(final Document doc) throws IOException {
- final Document d;
- try {
- d = newBuilder().newDocument();
- } catch (ParserConfigurationException e) {
- throw new IOException("Cannot clone document");
- }
- final Node n = d.importNode(doc.getDocumentElement(), true);
- d.appendChild(n);
- return d;
- }
-
- /** Parse an XHTML file from our ServletContext and return the instance. */
- public static Document parseFile(final ServletContext context,
- final String name) throws IOException {
- final InputStream in;
-
- in = context.getResourceAsStream(name);
- if (in == null) {
- return null;
- }
- try {
- try {
- try {
- return newBuilder().parse(in);
- } catch (SAXException e) {
- throw new IOException("Error reading " + name, e);
- } catch (ParserConfigurationException e) {
- throw new IOException("Error reading " + name, e);
- }
- } finally {
- in.close();
- }
- } catch (IOException e) {
- throw new IOException("Error reading " + name, e);
- }
- }
-
- /** Read a Read a UTF-8 text file from our ServletContext and return it. */
- public static String readFile(final ServletContext context, final String name)
- throws IOException {
- final InputStream in = context.getResourceAsStream(name);
- if (in == null) {
- return null;
- }
- try {
- return asString(in);
- } catch (IOException e) {
- throw new IOException("Error reading " + name, e);
- }
- }
-
- /** Parse an XHTML file from the local drive and return the instance. */
- public static Document parseFile(final File parentDir, final String name)
- throws IOException {
- if (parentDir == null) {
- return null;
- }
- final File path = new File(parentDir, name);
- try {
- final InputStream in = new FileInputStream(path);
- try {
- try {
- return newBuilder().parse(in);
- } catch (SAXException e) {
- throw new IOException("Error reading " + path, e);
- } catch (ParserConfigurationException e) {
- throw new IOException("Error reading " + path, e);
- }
- } finally {
- in.close();
- }
- } catch (FileNotFoundException e) {
- return null;
- } catch (IOException e) {
- throw new IOException("Error reading " + path, e);
- }
- }
-
- /** Read a UTF-8 text file from the local drive. */
- public static String readFile(final File parentDir, final String name)
- throws IOException {
- if (parentDir == null) {
- return null;
- }
- final File path = new File(parentDir, name);
- try {
- return asString(new FileInputStream(path));
- } catch (FileNotFoundException e) {
- return null;
- } catch (IOException e) {
- throw new IOException("Error reading " + path, e);
- }
- }
-
- private static String asString(final InputStream in)
- throws UnsupportedEncodingException, IOException {
- try {
- final StringBuilder w = new StringBuilder();
- final InputStreamReader r = new InputStreamReader(in, ENC);
- final char[] buf = new char[512];
- int n;
- while ((n = r.read(buf)) > 0) {
- w.append(buf, 0, n);
- }
- return w.toString();
- } finally {
- in.close();
- }
- }
-
- private static DocumentBuilder newBuilder()
- throws ParserConfigurationException {
- final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setValidating(false);
- factory.setExpandEntityReferences(false);
- factory.setIgnoringComments(true);
- final DocumentBuilder parser = factory.newDocumentBuilder();
- return parser;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpAuthFilter.java b/src/main/java/com/google/gerrit/server/http/HttpAuthFilter.java
deleted file mode 100644
index 3b2e93acab..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpAuthFilter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gwt.user.server.rpc.RPCServletUtils;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Watches request for the host page and requires login if not yet signed in.
- * <p>
- * If HTTP authentication has been enabled on this server this filter is bound
- * in front of the {@link HostPageServlet} and redirects users who are not yet
- * signed in to visit {@code /login/}, so the web container can force login.
- * This redirect is performed with JavaScript, such that any existing anchor
- * token in the URL can be rewritten and preserved through the authentication
- * process of any enterprise single sign-on solutions.
- */
-@Singleton
-class HttpAuthFilter implements Filter {
- private final Provider<WebSession> webSession;
- private final byte[] signInRaw;
- private final byte[] signInGzip;
-
- @Inject
- HttpAuthFilter(final Provider<WebSession> webSession,
- final ServletContext servletContext) throws IOException {
- this.webSession = webSession;
-
- final String hostPageName = "WEB-INF/LoginRedirect.html";
- final String doc = HtmlDomUtil.readFile(servletContext, "/" + hostPageName);
- if (doc == null) {
- throw new FileNotFoundException("No " + hostPageName + " in webapp");
- }
-
- signInRaw = doc.getBytes(HtmlDomUtil.ENC);
- signInGzip = HtmlDomUtil.compress(signInRaw);
- }
-
- @Override
- public void doFilter(final ServletRequest request,
- final ServletResponse response, final FilterChain chain)
- throws IOException, ServletException {
- if (!webSession.get().isSignedIn()) {
- // Not signed in yet. Since the browser state might have an anchor
- // token which we want to capture and carry through the auth process
- // we send back JavaScript now to capture that, and do the real work
- // of redirecting to the authentication area.
- //
- final HttpServletRequest req = (HttpServletRequest) request;
- final HttpServletResponse rsp = (HttpServletResponse) response;
- final byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
- rsp.setHeader("Content-Encoding", "gzip");
- tosend = signInGzip;
- } else {
- tosend = signInRaw;
- }
-
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC);
- rsp.setContentLength(tosend.length);
- final OutputStream out = rsp.getOutputStream();
- try {
- out.write(tosend);
- } finally {
- out.close();
- }
- } else {
- // Already signed in, forward the request.
- //
- chain.doFilter(request, response);
- }
- }
-
- @Override
- public void init(final FilterConfig filterConfig) {
- }
-
- @Override
- public void destroy() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpAuthModule.java b/src/main/java/com/google/gerrit/server/http/HttpAuthModule.java
deleted file mode 100644
index ec2ba20f41..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpAuthModule.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.inject.servlet.ServletModule;
-
-/** Servlets and support related to HTTP authentication. */
-class HttpAuthModule extends ServletModule {
- @Override
- protected void configureServlets() {
- filter("/").through(HttpAuthFilter.class);
- serve("/login/*").with(HttpLoginServlet.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpCanonicalWebUrlProvider.java b/src/main/java/com/google/gerrit/server/http/HttpCanonicalWebUrlProvider.java
deleted file mode 100644
index 013da83c49..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpCanonicalWebUrlProvider.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.CanonicalWebUrlProvider;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-import org.eclipse.jgit.lib.Config;
-
-import javax.servlet.http.HttpServletRequest;
-
-/** Sets {@link CanonicalWebUrl} to current HTTP request if not configured. */
-class HttpCanonicalWebUrlProvider extends CanonicalWebUrlProvider {
- private Provider<HttpServletRequest> requestProvider;
-
- @Inject
- HttpCanonicalWebUrlProvider(@GerritServerConfig final Config config) {
- super(config);
- }
-
- @Inject(optional = true)
- void setHttpServletRequest(final Provider<HttpServletRequest> hsr) {
- requestProvider = hsr;
- }
-
- @Override
- public String get() {
- String canonicalUrl = super.get();
- if (canonicalUrl != null) {
- return canonicalUrl;
- }
-
- if (requestProvider != null) {
- // No canonical URL configured? Maybe we can get a reasonable
- // guess from the incoming HTTP request, if we are currently
- // inside of an HTTP request scope.
- //
- final HttpServletRequest req;
- try {
- req = requestProvider.get();
- } catch (ProvisionException noWeb) {
- if (noWeb.getCause() instanceof OutOfScopeException) {
- // We can't obtain the request as we are not inside of
- // an HTTP request scope. Callers must handle null.
- //
- return null;
- } else {
- throw noWeb;
- }
- }
-
- final StringBuffer url = req.getRequestURL();
- url.setLength(url.length() - req.getServletPath().length());
- if (url.charAt(url.length() - 1) != '/') {
- url.append('/');
- }
- return url.toString();
- }
-
- // We have no way of guessing our HTTP url.
- //
- return null;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpCurrentUserProvider.java b/src/main/java/com/google/gerrit/server/http/HttpCurrentUserProvider.java
deleted file mode 100644
index 859105e8b3..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpCurrentUserProvider.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.CurrentUser;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.servlet.RequestScoped;
-
-@RequestScoped
-class HttpCurrentUserProvider implements Provider<CurrentUser> {
- private final WebSession session;
-
- @Inject
- HttpCurrentUserProvider(final WebSession session) {
- this.session = session;
- }
-
- @Override
- public CurrentUser get() {
- return session.getCurrentUser();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpIdentifiedUserProvider.java b/src/main/java/com/google/gerrit/server/http/HttpIdentifiedUserProvider.java
deleted file mode 100644
index 9961acca1f..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpIdentifiedUserProvider.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.rpc.NotSignedInException;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.RequestScoped;
-
-@RequestScoped
-class HttpIdentifiedUserProvider implements Provider<IdentifiedUser> {
- private final CurrentUser user;
-
- @Inject
- HttpIdentifiedUserProvider(final CurrentUser u) {
- user = u;
- }
-
- @Override
- public IdentifiedUser get() {
- if (user instanceof IdentifiedUser) {
- return (IdentifiedUser) user;
- }
- throw new ProvisionException(NotSignedInException.MESSAGE,
- new NotSignedInException());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpLoginServlet.java b/src/main/java/com/google/gerrit/server/http/HttpLoginServlet.java
deleted file mode 100644
index 665ff3f948..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpLoginServlet.java
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.AuthRequest;
-import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.util.Base64;
-
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Initializes the user session if HTTP authentication is enabled.
- * <p>
- * If HTTP authentication has been enabled this servlet binds to {@code /login/}
- * and initializes the user session based on user information contained in the
- * HTTP request.
- */
-@Singleton
-class HttpLoginServlet extends HttpServlet {
- private static final Logger log =
- LoggerFactory.getLogger(HttpLoginServlet.class);
-
- private static final String AUTHORIZATION = "Authorization";
- private final Provider<WebSession> webSession;
- private final Provider<String> urlProvider;
- private final AccountManager accountManager;
- private final String loginHeader;
-
- @Inject
- HttpLoginServlet(final AuthConfig authConfig,
- final Provider<WebSession> webSession,
- @CanonicalWebUrl @Nullable final Provider<String> urlProvider,
- final AccountManager accountManager) {
- this.webSession = webSession;
- this.urlProvider = urlProvider;
- this.accountManager = accountManager;
-
- final String hdr = authConfig.getLoginHttpHeader();
- this.loginHeader = hdr != null && !hdr.equals("") ? hdr : AUTHORIZATION;
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws ServletException, IOException {
- final String token = getToken(req);
- if ("logout".equals(token) || "signout".equals(token)) {
- req.getRequestDispatcher("/logout").forward(req, rsp);
- return;
- }
-
- final String user = getRemoteUser(req);
- if (user == null || "".equals(user)) {
- log.error("Unable to authenticate user by " + loginHeader
- + " request header. Check container or server configuration.");
- rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
- }
-
- final AuthRequest areq = AuthRequest.forUser(user);
- final AuthResult arsp;
- try {
- arsp = accountManager.authenticate(areq);
- } catch (AccountException e) {
- log.error("Unable to authenticate user \"" + user + "\"", e);
- rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
- return;
- }
-
- final StringBuilder rdr = new StringBuilder();
- rdr.append(urlProvider.get());
- rdr.append('#');
- if (arsp.isNew() && !token.startsWith(Link.REGISTER + ",")) {
- rdr.append(Link.REGISTER);
- rdr.append(',');
- }
- rdr.append(token);
-
- webSession.get().login(arsp.getAccountId(), false);
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.sendRedirect(rdr.toString());
- }
-
- private String getToken(final HttpServletRequest req) {
- String token = req.getPathInfo();
- if (token != null && token.startsWith("/")) {
- token = token.substring(1);
- }
- if (token == null || token.isEmpty()) {
- token = Link.MINE;
- }
- return token;
- }
-
- private String getRemoteUser(final HttpServletRequest req) {
- if (AUTHORIZATION.equals(loginHeader)) {
- final String user = req.getRemoteUser();
- if (user != null && !"".equals(user)) {
- // The container performed the authentication, and has the user
- // identity already decoded for us. Honor that as we have been
- // configured to honor HTTP authentication.
- //
- return user;
- }
-
- // If the container didn't do the authentication we might
- // have done it in the front-end web server. Try to split
- // the identity out of the Authorization header and honor it.
- //
- String auth = req.getHeader(AUTHORIZATION);
- if (auth == null || "".equals(auth)) {
- return null;
-
- } else if (auth.startsWith("Basic ")) {
- auth = auth.substring("Basic ".length());
- auth = new String(Base64.decode(auth));
- final int c = auth.indexOf(':');
- return c > 0 ? auth.substring(0, c) : null;
-
- } else if (auth.startsWith("Digest ")) {
- final int u = auth.indexOf("username=\"");
- if (u <= 0) {
- return null;
- }
- auth = auth.substring(u + 10);
- final int e = auth.indexOf('"');
- return e > 0 ? auth.substring(0, auth.indexOf('"')) : null;
-
- } else {
- return null;
- }
- } else {
- // Nonstandard HTTP header. We have been told to trust this
- // header blindly as-is.
- //
- final String user = req.getHeader(loginHeader);
- return user != null && !"".equals(user) ? user : null;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpLogoutServlet.java b/src/main/java/com/google/gerrit/server/http/HttpLogoutServlet.java
deleted file mode 100644
index f399d55c9c..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpLogoutServlet.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@Singleton
-class HttpLogoutServlet extends HttpServlet {
- private final Provider<WebSession> webSession;
- private final Provider<String> urlProvider;
- private final String logoutUrl;
-
- @Inject
- HttpLogoutServlet(final AuthConfig authConfig,
- final Provider<WebSession> webSession,
- @CanonicalWebUrl @Nullable final Provider<String> urlProvider,
- final AccountManager accountManager) {
- this.webSession = webSession;
- this.urlProvider = urlProvider;
- this.logoutUrl = authConfig.getLogoutURL();
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- webSession.get().logout();
- if (logoutUrl != null) {
- rsp.sendRedirect(logoutUrl);
- } else {
- rsp.sendRedirect(urlProvider.get());
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/HttpRemotePeerProvider.java b/src/main/java/com/google/gerrit/server/http/HttpRemotePeerProvider.java
deleted file mode 100644
index a8ce141019..0000000000
--- a/src/main/java/com/google/gerrit/server/http/HttpRemotePeerProvider.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.RequestScoped;
-
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-
-import javax.servlet.http.HttpServletRequest;
-
-@RequestScoped
-class HttpRemotePeerProvider implements Provider<SocketAddress> {
- private final HttpServletRequest req;
-
- @Inject
- HttpRemotePeerProvider(final HttpServletRequest r) {
- req = r;
- }
-
- @Override
- public SocketAddress get() {
- final String addr = req.getRemoteAddr();
- final int port = req.getRemotePort();
- try {
- return new InetSocketAddress(InetAddress.getByName(addr), port);
- } catch (UnknownHostException e) {
- throw new ProvisionException("Cannot get @RemotePeer", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/LegacyGerritServlet.java b/src/main/java/com/google/gerrit/server/http/LegacyGerritServlet.java
deleted file mode 100644
index f5e5818007..0000000000
--- a/src/main/java/com/google/gerrit/server/http/LegacyGerritServlet.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gwt.user.server.rpc.RPCServletUtils;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Redirects from {@code /Gerrit#foo} to {@code /#foo} in JavaScript.
- * <p>
- * This redirect exists to convert the older /Gerrit URL into the more modern
- * URL format which does not use a servlet name for the host page. We cannot do
- * the redirect here in the server side, as it would lose any history token that
- * appears in the URL. Instead we send an HTML page which instructs the browser
- * to replace the URL, but preserve the history token.
- */
-@SuppressWarnings("serial")
-@Singleton
-public class LegacyGerritServlet extends HttpServlet {
- private final byte[] raw;
- private final byte[] compressed;
-
- @Inject
- LegacyGerritServlet(final ServletContext servletContext) throws IOException {
- final String hostPageName = "WEB-INF/LegacyGerrit.html";
- final String doc = HtmlDomUtil.readFile(servletContext, "/" + hostPageName);
- if (doc == null) {
- throw new FileNotFoundException("No " + hostPageName + " in webapp");
- }
-
- raw = doc.getBytes(HtmlDomUtil.ENC);
- compressed = HtmlDomUtil.compress(raw);
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final byte[] tosend;
- if (RPCServletUtils.acceptsGzipEncoding(req)) {
- rsp.setHeader("Content-Encoding", "gzip");
- tosend = compressed;
- } else {
- tosend = raw;
- }
-
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.setContentType("text/html");
- rsp.setCharacterEncoding(HtmlDomUtil.ENC);
- rsp.setContentLength(tosend.length);
- final OutputStream out = rsp.getOutputStream();
- try {
- out.write(tosend);
- } finally {
- out.close();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/PrettifyServlet.java b/src/main/java/com/google/gerrit/server/http/PrettifyServlet.java
deleted file mode 100644
index eb94f6f0c7..0000000000
--- a/src/main/java/com/google/gerrit/server/http/PrettifyServlet.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@Singleton
-public class PrettifyServlet extends HttpServlet {
- private static final String VERSION = "20090521";
-
- private final byte[] content;
-
- @Inject
- PrettifyServlet(final ServletContext servletContext) throws IOException {
- final String myDir = "/gerrit/prettify" + VERSION + "/";
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- load(buffer, servletContext, myDir + "prettify.js");
- for (Object p : servletContext.getResourcePaths(myDir)) {
- String name = (String) p;
- if (name.startsWith(myDir + "lang-") && name.endsWith(".js")) {
- load(buffer, servletContext, name);
- }
- }
- content = buffer.toByteArray();
- }
-
- private void load(final OutputStream buffer,
- final ServletContext servletContext, final String path)
- throws IOException {
- final InputStream in = servletContext.getResourceAsStream(path);
- if (in != null) {
- try {
- final byte[] tmp = new byte[4096];
- int cnt;
- while ((cnt = in.read(tmp)) > 0) {
- buffer.write(tmp, 0, cnt);
- }
- buffer.write(';');
- buffer.write('\n');
- in.close();
- } catch (IOException e) {
- throw new IOException("Cannot read " + path, e);
- }
- }
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final String want = req.getPathInfo();
- if (want.equals("/" + VERSION + ".js")) {
- final long now = System.currentTimeMillis();
- rsp.setHeader("Cache-Control", "max-age=31536000,public");
- rsp.setDateHeader("Expires", now + 31536000000L);
- rsp.setDateHeader("Date", now);
- rsp.setContentType("application/x-javascript");
- rsp.setContentLength(content.length);
- rsp.getOutputStream().write(content);
- } else {
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.setDateHeader("Date", System.currentTimeMillis());
- rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/RequestCleanupFilter.java b/src/main/java/com/google/gerrit/server/http/RequestCleanupFilter.java
deleted file mode 100644
index d3e28d042f..0000000000
--- a/src/main/java/com/google/gerrit/server/http/RequestCleanupFilter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.RequestCleanup;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-/** Executes any pending {@link RequestCleanup} at the end of a request. */
-@Singleton
-class RequestCleanupFilter implements Filter {
- private final Provider<RequestCleanup> cleanup;
-
- @Inject
- RequestCleanupFilter(final Provider<RequestCleanup> r) {
- cleanup = r;
- }
-
- @Override
- public void init(FilterConfig filterConfig) {
- }
-
- @Override
- public void destroy() {
- }
-
- @Override
- public void doFilter(final ServletRequest request,
- final ServletResponse response, final FilterChain chain)
- throws IOException, ServletException {
- try {
- chain.doFilter(request, response);
- } finally {
- cleanup.get().run();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/RpcServletModule.java b/src/main/java/com/google/gerrit/server/http/RpcServletModule.java
deleted file mode 100644
index a13de8be8d..0000000000
--- a/src/main/java/com/google/gerrit/server/http/RpcServletModule.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gwtjsonrpc.client.RemoteJsonService;
-import com.google.inject.Key;
-import com.google.inject.Scopes;
-import com.google.inject.internal.UniqueAnnotations;
-import com.google.inject.servlet.ServletModule;
-
-/** Binds {@link RemoteJsonService} implementations to a JSON servlet. */
-public abstract class RpcServletModule extends ServletModule {
- private final String prefix;
-
- protected RpcServletModule(final String pathPrefix) {
- prefix = pathPrefix;
- }
-
- protected void rpc(Class<? extends RemoteJsonService> clazz) {
- String name = clazz.getSimpleName();
- if (name.endsWith("Impl")) {
- name = name.substring(0, name.length() - 4);
- }
- rpc(name, clazz);
- }
-
- protected void rpc(final String name, Class<? extends RemoteJsonService> clazz) {
- final Key<GerritJsonServlet> srv =
- Key.get(GerritJsonServlet.class, UniqueAnnotations.create());
- final GerritJsonServletProvider provider =
- new GerritJsonServletProvider(clazz);
- bind(clazz);
- serve(prefix + name).with(srv);
- bind(srv).toProvider(provider).in(Scopes.SINGLETON);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/SshServlet.java b/src/main/java/com/google/gerrit/server/http/SshServlet.java
deleted file mode 100644
index 7756e5ffda..0000000000
--- a/src/main/java/com/google/gerrit/server/http/SshServlet.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Servlet hosting an SSH daemon on another port. During a standard HTTP GET
- * request the servlet returns the hostname and port number back to the client
- * in the form <code>${host} ${port}</code>.
- * <p>
- * Use a Git URL such as <code>ssh://${email}@${host}:${port}/${path}</code>,
- * e.g. <code>ssh://sop@google.com@gerrit.com:8010/tools/gerrit.git</code> to
- * access the SSH daemon itself.
- * <p>
- * Versions of Git before 1.5.3 may require setting the username and port
- * properties in the user's <code>~/.ssh/config</code> file, and using a host
- * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git:
- * <pre>
- * Host gerrit-alias
- * User sop@google.com
- * Hostname gerrit.com
- * Port 8010
- * </pre>
- */
-@SuppressWarnings("serial")
-@Singleton
-public class SshServlet extends HttpServlet {
- private final SshInfo sshd;
-
- @Inject
- SshServlet(final SshInfo daemon) {
- sshd = daemon;
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
-
- final InetSocketAddress addr = sshd.getAddress();
- final String out;
- if (addr != null) {
- final InetAddress ip = addr.getAddress();
- String host;
- if (ip != null && ip.isAnyLocalAddress()) {
- host = req.getServerName();
- } else if (ip instanceof Inet6Address) {
- host = "[" + addr.getHostName() + "]";
- } else {
- host = addr.getHostName();
- }
- out = host + " " + addr.getPort();
- } else {
- out = "NOT_AVAILABLE";
- }
-
- rsp.setCharacterEncoding("UTF-8");
- rsp.setContentType("text/plain");
- final PrintWriter w = rsp.getWriter();
- try {
- w.write(out);
- } finally {
- w.close();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/StaticServlet.java b/src/main/java/com/google/gerrit/server/http/StaticServlet.java
deleted file mode 100644
index a075c5eafa..0000000000
--- a/src/main/java/com/google/gerrit/server/http/StaticServlet.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import com.google.gerrit.server.config.SitePath;
-import com.google.gwt.user.server.rpc.RPCServletUtils;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.util.NB;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.zip.GZIPOutputStream;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/** Sends static content from the site 's <code>static/</code> subdirectory. */
-@SuppressWarnings("serial")
-@Singleton
-public class StaticServlet extends HttpServlet {
- private static final long MAX_AGE = 12 * 60 * 60 * 1000L/* milliseconds */;
- private static final String CACHE_CTRL =
- "public, max-age=" + (MAX_AGE / 1000L);
-
- private static final HashMap<String, String> MIME_TYPES =
- new HashMap<String, String>();
- static {
- MIME_TYPES.put("html", "text/html");
- MIME_TYPES.put("htm", "text/html");
- MIME_TYPES.put("js", "application/x-javascript");
- MIME_TYPES.put("css", "text/css");
- MIME_TYPES.put("rtf", "text/rtf");
- MIME_TYPES.put("txt", "text/plain");
- MIME_TYPES.put("text", "text/plain");
- MIME_TYPES.put("pdf", "application/pdf");
- MIME_TYPES.put("jpeg", "image/jpeg");
- MIME_TYPES.put("jpg", "image/jpeg");
- MIME_TYPES.put("gif", "image/gif");
- MIME_TYPES.put("png", "image/png");
- MIME_TYPES.put("tiff", "image/tiff");
- MIME_TYPES.put("tif", "image/tiff");
- MIME_TYPES.put("svg", "image/svg+xml");
- }
-
- private static String contentType(final String name) {
- final int dot = name.lastIndexOf('.');
- final String ext = 0 < dot ? name.substring(dot + 1) : "";
- final String type = MIME_TYPES.get(ext);
- return type != null ? type : "application/octet-stream";
- }
-
- private static byte[] readFile(final File p) throws IOException {
- final FileInputStream in = new FileInputStream(p);
- try {
- final byte[] r = new byte[(int) in.getChannel().size()];
- NB.readFully(in, r, 0, r.length);
- return r;
- } finally {
- in.close();
- }
- }
-
- private static byte[] compress(final byte[] raw) throws IOException {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- final GZIPOutputStream gz = new GZIPOutputStream(out);
- gz.write(raw);
- gz.finish();
- gz.flush();
- return out.toByteArray();
- }
-
- private final File staticBase;
-
- @Inject
- StaticServlet(@SitePath final File sitePath) {
- staticBase = new File(sitePath, "static");
- }
-
- private File local(final HttpServletRequest req) {
- final String name = req.getPathInfo();
- if (name.length() < 2 || !name.startsWith("/")) {
- // Too short to be a valid file name, or doesn't start with
- // the path info separator like we expected.
- //
- return null;
- }
-
- if (name.indexOf('/', 1) > 0 || name.indexOf('\\', 1) > 0) {
- // Contains a path separator. Don't serve it as the client
- // might be trying something evil like "/../../etc/passwd".
- // This static servlet is just meant to facilitate simple
- // assets like banner images.
- //
- return null;
- }
-
- final File p = new File(staticBase, name.substring(1));
- return p.isFile() ? p : null;
- }
-
- @Override
- protected long getLastModified(final HttpServletRequest req) {
- final File p = local(req);
- return p != null ? p.lastModified() : -1;
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final File p = local(req);
- if (p == null) {
- rsp.setStatus(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- final String type = contentType(p.getName());
- final byte[] tosend;
- if (!type.equals("application/x-javascript")
- && RPCServletUtils.acceptsGzipEncoding(req)) {
- rsp.setHeader("Content-Encoding", "gzip");
- tosend = compress(readFile(p));
- } else {
- tosend = readFile(p);
- }
-
- rsp.setHeader("Cache-Control", CACHE_CTRL);
- rsp.setDateHeader("Expires", System.currentTimeMillis() + MAX_AGE);
- rsp.setDateHeader("Last-Modified", p.lastModified());
- rsp.setContentType(type);
- rsp.setContentLength(tosend.length);
- final OutputStream out = rsp.getOutputStream();
- try {
- out.write(tosend);
- } finally {
- out.close();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/UrlModule.java b/src/main/java/com/google/gerrit/server/http/UrlModule.java
deleted file mode 100644
index 7be9708e72..0000000000
--- a/src/main/java/com/google/gerrit/server/http/UrlModule.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gwtexpui.server.CacheControlFilter;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.internal.UniqueAnnotations;
-import com.google.inject.servlet.ServletModule;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-class UrlModule extends ServletModule {
- @Override
- protected void configureServlets() {
- filter("/*").through(Key.get(CacheControlFilter.class));
- bind(Key.get(CacheControlFilter.class)).in(SINGLETON);
-
- serve("/").with(HostPageServlet.class);
- serve("/Gerrit").with(LegacyGerritServlet.class);
- serve("/Gerrit/*").with(legacyGerritScreen());
- serve("/cat/*").with(CatServlet.class);
- serve("/logout").with(HttpLogoutServlet.class);
- serve("/prettify/*").with(PrettifyServlet.class);
- serve("/signout").with(HttpLogoutServlet.class);
- serve("/ssh_info").with(SshServlet.class);
- serve("/static/*").with(StaticServlet.class);
-
- serve("/all").with(screen(Link.ALL_MERGED));
- serve("/mine").with(screen(Link.MINE));
- serve("/open").with(screen(Link.ALL_OPEN));
- serve("/settings").with(screen(Link.SETTINGS));
- serve("/starred").with(screen(Link.MINE_STARRED));
-
- serveRegex( //
- "^/([1-9][0-9]*)/?$", //
- "^/r/([0-9a-fA-F]{4," + RevId.LEN + "})/?$" //
- ).with(changeQuery());
- }
-
- private Key<HttpServlet> screen(final String target) {
- return key(new HttpServlet() {
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- toGerrit(target, req, rsp);
- }
- });
- }
-
- private Key<HttpServlet> legacyGerritScreen() {
- return key(new HttpServlet() {
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final String token = req.getPathInfo().substring(1);
- toGerrit(token, req, rsp);
- }
- });
- }
-
- private Key<HttpServlet> changeQuery() {
- return key(new HttpServlet() {
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- toGerrit(Link.toChangeQuery(req.getPathInfo()), req, rsp);
- }
- });
- }
-
- private Key<HttpServlet> key(final HttpServlet servlet) {
- final Key<HttpServlet> srv =
- Key.get(HttpServlet.class, UniqueAnnotations.create());
- bind(srv).toProvider(new Provider<HttpServlet>() {
- @Override
- public HttpServlet get() {
- return servlet;
- }
- }).in(SINGLETON);
- return srv;
- }
-
- private void toGerrit(final String target, final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final StringBuilder url = new StringBuilder();
- url.append(req.getContextPath());
- url.append('/');
- url.append('#');
- url.append(target);
- rsp.sendRedirect(url.toString());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/WebModule.java b/src/main/java/com/google/gerrit/server/http/WebModule.java
deleted file mode 100644
index b0cd1569cd..0000000000
--- a/src/main/java/com/google/gerrit/server/http/WebModule.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.data.GerritConfig;
-import com.google.gerrit.client.reviewdb.AuthType;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.RemotePeer;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.config.GerritRequestModule;
-import com.google.gerrit.server.contact.ContactStore;
-import com.google.gerrit.server.contact.ContactStoreProvider;
-import com.google.gerrit.server.ldap.LdapAuthModule;
-import com.google.gerrit.server.openid.OpenIdModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.servlet.RequestScoped;
-import com.google.inject.servlet.ServletModule;
-
-import java.net.SocketAddress;
-
-class WebModule extends FactoryModule {
- private final Provider<SshInfo> sshInfoProvider;
- private final AuthType loginType;
-
- @Inject
- WebModule(final Provider<SshInfo> sshInfoProvider, final AuthConfig authConfig) {
- this(sshInfoProvider, authConfig.getAuthType());
- }
-
- WebModule(final Provider<SshInfo> sshInfoProvider, final AuthType loginType) {
- this.sshInfoProvider = sshInfoProvider;
- this.loginType = loginType;
- }
-
- @Override
- protected void configure() {
- install(new ServletModule() {
- @Override
- protected void configureServlets() {
- filter("/*").through(RequestCleanupFilter.class);
- }
- });
-
- switch (loginType) {
- case OPENID:
- install(new OpenIdModule());
- break;
-
- case HTTP:
- case HTTP_LDAP:
- install(new HttpAuthModule());
- break;
-
- case LDAP:
- install(new LdapAuthModule());
- break;
-
- case DEVELOPMENT_BECOME_ANY_ACCOUNT:
- install(new ServletModule() {
- @Override
- protected void configureServlets() {
- serve("/become").with(BecomeAnyAccountLoginServlet.class);
- }
- });
- break;
-
- default:
- throw new ProvisionException("Unsupported loginType: " + loginType);
- }
-
- install(new UrlModule());
- install(new UiRpcModule());
- install(new GerritRequestModule());
-
- bind(SshInfo.class).toProvider(sshInfoProvider);
- bind(ContactStore.class).toProvider(ContactStoreProvider.class).in(
- SINGLETON);
- bind(GerritConfig.class).toProvider(GerritConfigProvider.class).in(
- SINGLETON);
- bind(AccountManager.class).in(SINGLETON);
- bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
- HttpRemotePeerProvider.class).in(RequestScoped.class);
-
- install(WebSession.module());
-
- bind(CurrentUser.class).toProvider(HttpCurrentUserProvider.class).in(
- RequestScoped.class);
- bind(IdentifiedUser.class).toProvider(HttpIdentifiedUserProvider.class).in(
- RequestScoped.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/WebSession.java b/src/main/java/com/google/gerrit/server/http/WebSession.java
deleted file mode 100644
index 7ec63d1819..0000000000
--- a/src/main/java/com/google/gerrit/server/http/WebSession.java
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import static java.util.concurrent.TimeUnit.HOURS;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.AccessPath;
-import com.google.gerrit.server.AnonymousUser;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EvictionPolicy;
-import com.google.gerrit.server.http.WebSessionManager.Key;
-import com.google.gerrit.server.http.WebSessionManager.Val;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.TypeLiteral;
-import com.google.inject.servlet.RequestScoped;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@RequestScoped
-public final class WebSession {
- private static final String ACCOUNT_COOKIE = "GerritAccount";
-
- static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final String cacheName = WebSessionManager.CACHE_NAME;
- final TypeLiteral<Cache<Key, Val>> type =
- new TypeLiteral<Cache<Key, Val>>() {};
- disk(type, cacheName) //
- .memoryLimit(1024) // reasonable default for many sites
- .maxAge(12, HOURS) // expire sessions if they are inactive
- .evictionPolicy(EvictionPolicy.LRU) // keep most recently used
- ;
- bind(WebSessionManager.class);
- bind(WebSession.class).in(RequestScoped.class);
- }
- };
- }
-
- private final HttpServletRequest request;
- private final HttpServletResponse response;
- private final WebSessionManager manager;
- private final AnonymousUser anonymous;
- private final IdentifiedUser.RequestFactory identified;
- private Cookie outCookie;
-
- private Key key;
- private Val val;
-
- @Inject
- WebSession(final HttpServletRequest request,
- final HttpServletResponse response, final WebSessionManager manager,
- final AnonymousUser anonymous,
- final IdentifiedUser.RequestFactory identified) {
- this.request = request;
- this.response = response;
- this.manager = manager;
- this.anonymous = anonymous;
- this.identified = identified;
-
- final String cookie = readCookie();
- if (cookie != null) {
- key = new Key(cookie);
- val = manager.get(key);
- } else {
- key = null;
- val = null;
- }
-
- if (isSignedIn() && val.needsCookieRefresh()) {
- // Cookie is more than half old. Send the cookie again to the
- // client with an updated expiration date. We don't dare to
- // change the key token here because there may be other RPCs
- // queued up in the browser whose xsrfKey would not get updated
- // with the new token, causing them to fail.
- //
- val = manager.createVal(key, val);
- saveCookie();
- }
- }
-
- private String readCookie() {
- final Cookie[] all = request.getCookies();
- if (all != null) {
- for (final Cookie c : all) {
- if (ACCOUNT_COOKIE.equals(c.getName())) {
- final String v = c.getValue();
- return v != null && !"".equals(v) ? v : null;
- }
- }
- }
- return null;
- }
-
- public boolean isSignedIn() {
- return val != null;
- }
-
- String getToken() {
- return isSignedIn() ? key.getToken() : null;
- }
-
- boolean isTokenValid(final String inputToken) {
- return isSignedIn() && key.getToken().equals(inputToken);
- }
-
- CurrentUser getCurrentUser() {
- if (isSignedIn()) {
- return identified.create(AccessPath.WEB, val.getAccountId());
- }
- return anonymous;
- }
-
- public void login(final Account.Id id, final boolean rememberMe) {
- logout();
-
- key = manager.createKey(id);
- val = manager.createVal(key, id, rememberMe);
- saveCookie();
- }
-
- public void logout() {
- if (val != null) {
- manager.destroy(key);
- key = null;
- val = null;
- saveCookie();
- }
- }
-
- private void saveCookie() {
- final String token;
- final int ageSeconds;
-
- if (key == null) {
- token = "";
- ageSeconds = 0 /* erase at client */;
- } else {
- token = key.getToken();
- ageSeconds = manager.getCookieAge(val);
- }
-
- if (outCookie == null) {
- String path = request.getContextPath();
- if (path.equals("")) {
- path = "/";
- }
- outCookie = new Cookie(ACCOUNT_COOKIE, token);
- outCookie.setPath(path);
- outCookie.setMaxAge(ageSeconds);
- response.addCookie(outCookie);
- } else {
- outCookie.setValue(token);
- outCookie.setMaxAge(ageSeconds);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/http/WebSessionManager.java b/src/main/java/com/google/gerrit/server/http/WebSessionManager.java
deleted file mode 100644
index e93cbf51c1..0000000000
--- a/src/main/java/com/google/gerrit/server/http/WebSessionManager.java
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.http;
-
-import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
-import static java.util.concurrent.TimeUnit.HOURS;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.cache.Cache;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.security.SecureRandom;
-
-@Singleton
-class WebSessionManager {
- static final String CACHE_NAME = "web_sessions";
-
- static long now() {
- return System.currentTimeMillis();
- }
-
- private final SecureRandom prng;
- private final Cache<Key, Val> self;
-
- @Inject
- WebSessionManager(@Named(CACHE_NAME) final Cache<Key, Val> cache) {
- prng = new SecureRandom();
- self = cache;
- }
-
- Key createKey(final Account.Id who) {
- try {
- final int nonceLen = 20;
- final ByteArrayOutputStream buf;
- final byte[] rnd = new byte[nonceLen];
- prng.nextBytes(rnd);
-
- buf = new ByteArrayOutputStream(3 + nonceLen);
- writeVarInt32(buf, (int) Key.serialVersionUID);
- writeVarInt32(buf, who.get());
- writeBytes(buf, rnd);
-
- return new Key(CookieBase64.encode(buf.toByteArray()));
- } catch (IOException e) {
- throw new RuntimeException("Cannot produce new account cookie", e);
- }
- }
-
- Val createVal(final Key key, final Val val) {
- return createVal(key, val.getAccountId(), val.isPersistentCookie());
- }
-
- Val createVal(final Key key, final Account.Id who, final boolean remember) {
- // Refresh the cookie every hour or when it is half-expired.
- // This reduces the odds that the user session will be kicked
- // early but also avoids us needing to refresh the cookie on
- // every single request.
- //
- final long halfAgeRefresh = self.getTimeToLive(MILLISECONDS) >>> 1;
- final long minRefresh = MILLISECONDS.convert(1, HOURS);
- final long refresh = Math.min(halfAgeRefresh, minRefresh);
- final long refreshCookieAt = now() + refresh;
-
- final Val val = new Val(who, refreshCookieAt, remember);
- self.put(key, val);
- return val;
- }
-
- int getCookieAge(final Val val) {
- if (val.isPersistentCookie()) {
- // Client may store the cookie until we would remove it from our
- // own cache, after which it will certainly be invalid.
- //
- return (int) self.getTimeToLive(SECONDS);
- } else {
- // Client should not store the cookie, as the user asked for us
- // to not remember them long-term. Sending -1 as the age will
- // cause the cookie to be only for this "browser session", which
- // is usually until the user exits their browser.
- //
- return -1;
- }
- }
-
- Val get(final Key key) {
- return self.get(key);
- }
-
- void destroy(final Key key) {
- self.remove(key);
- }
-
- static final class Key implements Serializable {
- static final long serialVersionUID = 2L;
-
- private transient String token;
-
- Key(final String t) {
- token = t;
- }
-
- String getToken() {
- return token;
- }
-
- @Override
- public int hashCode() {
- return token.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof Key && token.equals(((Key) obj).token);
- }
-
- private void writeObject(final ObjectOutputStream out) throws IOException {
- writeString(out, token);
- }
-
- private void readObject(final ObjectInputStream in) throws IOException {
- token = readString(in);
- }
- }
-
- static final class Val implements Serializable {
- static final long serialVersionUID = Key.serialVersionUID;
-
- private transient Account.Id accountId;
- private transient long refreshCookieAt;
- private transient boolean persistentCookie;
-
- Val(final Account.Id accountId, final long refreshCookieAt,
- final boolean persistentCookie) {
- this.accountId = accountId;
- this.refreshCookieAt = refreshCookieAt;
- this.persistentCookie = persistentCookie;
- }
-
- Account.Id getAccountId() {
- return accountId;
- }
-
- boolean needsCookieRefresh() {
- return refreshCookieAt <= now();
- }
-
- boolean isPersistentCookie() {
- return persistentCookie;
- }
-
- private void writeObject(final ObjectOutputStream out) throws IOException {
- writeVarInt32(out, 1);
- writeVarInt32(out, accountId.get());
-
- writeVarInt32(out, 2);
- writeFixInt64(out, refreshCookieAt);
-
- writeVarInt32(out, 3);
- writeVarInt32(out, persistentCookie ? 1 : 0);
-
- writeVarInt32(out, 0);
- }
-
- private void readObject(final ObjectInputStream in) throws IOException {
- PARSE: for (;;) {
- final int tag = readVarInt32(in);
- switch (tag) {
- case 0:
- break PARSE;
- case 1:
- accountId = new Account.Id(readVarInt32(in));
- continue;
- case 2:
- refreshCookieAt = readFixInt64(in);
- continue;
- case 3:
- persistentCookie = readVarInt32(in) != 0;
- continue;
- default:
- throw new IOException("Unknown tag found in object: " + tag);
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java b/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java
deleted file mode 100644
index a2d0887c3b..0000000000
--- a/src/main/java/com/google/gerrit/server/ioutil/BasicSerialization.java
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package com.google.gerrit.server.ioutil;
-
-import com.google.gerrit.client.rpc.CodedEnum;
-
-import org.eclipse.jgit.util.NB;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class BasicSerialization {
- private static final byte[] NO_BYTES = {};
-
- private static int safeRead(final InputStream input) throws IOException {
- final int b = input.read();
- if (b == -1) {
- throw new EOFException();
- }
- return b;
- }
-
- /** Read a fixed-width 64 bit integer in network byte order (big-endian). */
- public static long readFixInt64(final InputStream input) throws IOException {
- final long h = readFixInt32(input);
- final long l = readFixInt32(input) & 0xFFFFFFFFL;
- return (h << 32) | l;
- }
-
- /** Write a fixed-width 64 bit integer in network byte order (big-endian). */
- public static void writeFixInt64(final OutputStream output, final long val)
- throws IOException {
- writeFixInt32(output, (int) (val >>> 32));
- writeFixInt32(output, (int) (val & 0xFFFFFFFFL));
- }
-
- /** Read a fixed-width 32 bit integer in network byte order (big-endian). */
- public static int readFixInt32(final InputStream input) throws IOException {
- final int b1 = safeRead(input);
- final int b2 = safeRead(input);
- final int b3 = safeRead(input);
- final int b4 = safeRead(input);
- return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
- }
-
- /** Write a fixed-width 32 bit integer in network byte order (big-endian). */
- public static void writeFixInt32(final OutputStream output, final int val)
- throws IOException {
- output.write((val >>> 24) & 0xFF);
- output.write((val >>> 16) & 0xFF);
- output.write((val >>> 8) & 0xFF);
- output.write(val & 0xFF);
- }
-
- /** Read a varint from the input, one byte at a time. */
- public static int readVarInt32(final InputStream input) throws IOException {
- int result = 0;
- int offset = 0;
- for (; offset < 32; offset += 7) {
- final int b = safeRead(input);
- result |= (b & 0x7f) << offset;
- if ((b & 0x80) == 0) {
- return result;
- }
- }
- throw new EOFException();
- }
-
- /** Write a varint; value is treated as an unsigned value. */
- public static void writeVarInt32(final OutputStream output, int value)
- throws IOException {
- while (true) {
- if ((value & ~0x7F) == 0) {
- output.write(value);
- return;
- } else {
- output.write((value & 0x7F) | 0x80);
- value >>>= 7;
- }
- }
- }
-
- /** Read a fixed length byte array whose length is specified as a varint. */
- public static byte[] readBytes(final InputStream input) throws IOException {
- final int len = readVarInt32(input);
- if (len == 0) {
- return NO_BYTES;
- }
- final byte[] buf = new byte[len];
- NB.readFully(input, buf, 0, len);
- return buf;
- }
-
- /** Write a byte array prefixed by its length in a varint. */
- public static void writeBytes(final OutputStream output, final byte[] data)
- throws IOException {
- writeBytes(output, data, 0, data.length);
- }
-
- /** Write a byte array prefixed by its length in a varint. */
- public static void writeBytes(final OutputStream output, final byte[] data,
- final int offset, final int len) throws IOException {
- writeVarInt32(output, len);
- output.write(data, offset, len);
- }
-
- /** Read a UTF-8 string, prefixed by its byte length in a varint. */
- public static String readString(final InputStream input) throws IOException {
- final byte[] bin = readBytes(input);
- if (bin.length == 0) {
- return null;
- }
- return new String(bin, 0, bin.length, "UTF-8");
- }
-
- /** Write a UTF-8 string, prefixed by its byte length in a varint. */
- public static void writeString(final OutputStream output, final String s)
- throws IOException {
- if (s == null) {
- writeVarInt32(output, 0);
- } else {
- writeBytes(output, s.getBytes("UTF-8"));
- }
- }
-
- /** Read an enum whose code is stored as a varint. */
- public static <T extends CodedEnum> T readEnum(final InputStream input,
- final T[] all) throws IOException {
- final int val = readVarInt32(input);
- for (T t : all) {
- if (t.getCode() == val) {
- return t;
- }
- }
- throw new IOException("Invalid enum " + val + " for " + all[0].getClass());
- }
-
- /** Write an enum whose code is stored as a varint. */
- public static <T extends CodedEnum> void writeEnum(final OutputStream output,
- final T e) throws IOException {
- writeVarInt32(output, e.getCode());
- }
-
- private BasicSerialization() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ioutil/BlindSSLSocketFactory.java b/src/main/java/com/google/gerrit/server/ioutil/BlindSSLSocketFactory.java
deleted file mode 100644
index 853035b2d6..0000000000
--- a/src/main/java/com/google/gerrit/server/ioutil/BlindSSLSocketFactory.java
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ioutil;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
-import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.X509TrustManager;
-
-/** SSL socket factory that ignores SSL certificate validation. */
-public class BlindSSLSocketFactory extends SSLSocketFactory {
- private static final BlindSSLSocketFactory INSTANCE;
-
- static {
- final X509TrustManager dummyTrustManager = new X509TrustManager() {
- public X509Certificate[] getAcceptedIssuers() {
- return null;
- }
-
- public void checkClientTrusted(X509Certificate[] chain, String authType) {
- }
-
- public void checkServerTrusted(X509Certificate[] chain, String authType) {
- }
- };
-
- try {
- final SSLContext context = SSLContext.getInstance("SSL");
- final TrustManager[] trustManagers = {dummyTrustManager};
- final SecureRandom rng = new SecureRandom();
- context.init(null, trustManagers, rng);
- INSTANCE = new BlindSSLSocketFactory(context.getSocketFactory());
- } catch (GeneralSecurityException e) {
- throw new RuntimeException("Cannot create BlindSslSocketFactory", e);
- }
- }
-
- public static SocketFactory getDefault() {
- return INSTANCE;
- }
-
- private final SSLSocketFactory sslFactory;
-
- private BlindSSLSocketFactory(final SSLSocketFactory sslFactory) {
- this.sslFactory = sslFactory;
- }
-
- @Override
- public Socket createSocket(Socket s, String host, int port, boolean autoClose)
- throws IOException {
- return sslFactory.createSocket(s, host, port, autoClose);
- }
-
- @Override
- public String[] getDefaultCipherSuites() {
- return sslFactory.getDefaultCipherSuites();
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return sslFactory.getSupportedCipherSuites();
- }
-
- @Override
- public Socket createSocket() throws IOException {
- return sslFactory.createSocket();
- }
-
- @Override
- public Socket createSocket(String host, int port) throws IOException,
- UnknownHostException {
- return sslFactory.createSocket(host, port);
- }
-
- @Override
- public Socket createSocket(InetAddress host, int port) throws IOException {
- return sslFactory.createSocket(host, port);
- }
-
- @Override
- public Socket createSocket(String host, int port, InetAddress localHost,
- int localPort) throws IOException, UnknownHostException {
- return sslFactory.createSocket(host, port, localHost, localPort);
- }
-
- @Override
- public Socket createSocket(InetAddress address, int port,
- InetAddress localAddress, int localPort) throws IOException {
- return sslFactory.createSocket(address, port, localAddress, localPort);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapAuthModule.java b/src/main/java/com/google/gerrit/server/ldap/LdapAuthModule.java
deleted file mode 100644
index 660f7f02d6..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LdapAuthModule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-import com.google.inject.servlet.ServletModule;
-
-/** RPC support related to username/password LDAP authentication. */
-public class LdapAuthModule extends ServletModule {
- @Override
- protected void configureServlets() {
- serve("/login/*").with(LoginRedirectServlet.class);
- install(new RpcServletModule(UiRpcModule.PREFIX) {
- @Override
- protected void configureServlets() {
- rpc(UserPassAuthServiceImpl.class);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapModule.java b/src/main/java/com/google/gerrit/server/ldap/LdapModule.java
deleted file mode 100644
index d55e6515d0..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LdapModule.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import static java.util.concurrent.TimeUnit.HOURS;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.inject.Scopes;
-import com.google.inject.TypeLiteral;
-
-import java.util.Set;
-
-public class LdapModule extends CacheModule {
- static final String USERNAME_CACHE = "ldap_usernames";
- static final String GROUP_CACHE = "ldap_groups";
-
- @Override
- protected void configure() {
- final TypeLiteral<Cache<String, Set<AccountGroup.Id>>> groups =
- new TypeLiteral<Cache<String, Set<AccountGroup.Id>>>() {};
- final TypeLiteral<Cache<String, Account.Id>> usernames =
- new TypeLiteral<Cache<String, Account.Id>>() {};
-
- core(groups, GROUP_CACHE).maxAge(1, HOURS);
- core(usernames, USERNAME_CACHE);
- bind(Realm.class).to(LdapRealm.class).in(Scopes.SINGLETON);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapQuery.java b/src/main/java/com/google/gerrit/server/ldap/LdapQuery.java
deleted file mode 100644
index 3055fe6f5f..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LdapQuery.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import com.google.gerrit.server.ParamertizedString;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.BasicAttribute;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-
-/** Supports issuing parameterized queries against an LDAP data source. */
-class LdapQuery {
- static final Set<String> ALL_ATTRIBUTES = null;
-
- private final String base;
- private final SearchScope searchScope;
- private final ParamertizedString pattern;
- private final String[] returnAttributes;
-
- LdapQuery(final String base, final SearchScope searchScope,
- final ParamertizedString pattern, final Set<String> returnAttributes) {
- this.base = base;
- this.searchScope = searchScope;
-
- this.pattern = pattern;
-
- if (returnAttributes != null) {
- this.returnAttributes = new String[returnAttributes.size()];
- returnAttributes.toArray(this.returnAttributes);
- } else {
- this.returnAttributes = null;
- }
- }
-
- List<String> getParameters() {
- return pattern.getParameterNames();
- }
-
- List<Result> query(final DirContext ctx, final Map<String, String> params)
- throws NamingException {
- final SearchControls sc = new SearchControls();
- final NamingEnumeration<SearchResult> res;
-
- sc.setSearchScope(searchScope.scope());
- sc.setReturningAttributes(returnAttributes);
- res = ctx.search(base, pattern.getRawPattern(), pattern.bind(params), sc);
- try {
- final List<Result> r = new ArrayList<Result>();
- while (res.hasMore()) {
- r.add(new Result(res.next()));
- }
- return r;
- } finally {
- res.close();
- }
- }
-
- class Result {
- private final Map<String, Attribute> atts = new HashMap<String, Attribute>();
-
- Result(final SearchResult sr) {
- if (returnAttributes != null) {
- for (final String attName : returnAttributes) {
- final Attribute a = sr.getAttributes().get(attName);
- if (a != null && a.size() > 0) {
- atts.put(attName, a);
- }
- }
-
- } else {
- NamingEnumeration<? extends Attribute> e = sr.getAttributes().getAll();
- while (e.hasMoreElements()) {
- final Attribute a = e.nextElement();
- atts.put(a.getID(), a);
- }
- }
-
- atts.put("dn", new BasicAttribute("dn", sr.getNameInNamespace()));
- }
-
- String getDN() throws NamingException {
- return get("dn");
- }
-
- String get(final String attName) throws NamingException {
- final Attribute att = getAll(attName);
- return att != null && 0 < att.size() ? String.valueOf(att.get(0)) : null;
- }
-
- Attribute getAll(final String attName) {
- return atts.get(attName);
- }
-
- Set<String> attributes() {
- return Collections.unmodifiableSet(atts.keySet());
- }
-
- @Override
- public String toString() {
- try {
- return getDN();
- } catch (NamingException e) {
- return "";
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java b/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java
deleted file mode 100644
index 47aa33d359..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LdapRealm.java
+++ /dev/null
@@ -1,587 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AuthType;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.ParamertizedString;
-import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.AuthRequest;
-import com.google.gerrit.server.account.EmailExpander;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.ioutil.BlindSSLSocketFactory;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-
-import org.eclipse.jgit.lib.Config;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.InitialDirContext;
-import javax.net.ssl.SSLSocketFactory;
-
-@Singleton
-class LdapRealm implements Realm {
- private static final Logger log = LoggerFactory.getLogger(LdapRealm.class);
- private static final String LDAP = "com.sun.jndi.ldap.LdapCtxFactory";
- private static final String USERNAME = "username";
- private static final String GROUPNAME = "groupname";
-
- private final String server;
- private final String username;
- private final String password;
- private final LdapType type;
- private final boolean sslVerify;
-
- private final AuthConfig authConfig;
- private final SchemaFactory<ReviewDb> schema;
- private final EmailExpander emailExpander;
- private final ParamertizedString accountFullName;
- private final ParamertizedString accountEmailAddress;
- private final ParamertizedString accountSshUserName;
- private final String accountMemberField;
- private final List<LdapQuery> accountQueryList;
- private final SelfPopulatingCache<String, Account.Id> usernameCache;
-
- private final GroupCache groupCache;
- private boolean groupNeedsAccount;
- private final List<String> groupBases;
- private final SearchScope groupScope;
- private final ParamertizedString groupPattern;
- private final List<LdapQuery> groupMemberQueryList;
- private final SelfPopulatingCache<String, Set<AccountGroup.Id>> membershipCache;
-
- @Inject
- LdapRealm(
- final AuthConfig authConfig,
- final GroupCache groupCache,
- final EmailExpander emailExpander,
- final SchemaFactory<ReviewDb> schema,
- @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.Id>> rawGroup,
- @Named(LdapModule.USERNAME_CACHE) final Cache<String, Account.Id> rawUsername,
- @GerritServerConfig final Config config) {
- this.authConfig = authConfig;
- this.groupCache = groupCache;
- this.emailExpander = emailExpander;
- this.schema = schema;
-
- this.server = required(config, "server");
- this.username = optional(config, "username");
- this.password = optional(config, "password");
- this.sslVerify = config.getBoolean("ldap", "sslverify", true);
- this.type = discoverLdapType();
-
- groupMemberQueryList = new ArrayList<LdapQuery>();
- accountQueryList = new ArrayList<LdapQuery>();
-
- final Set<String> accountAtts = new HashSet<String>();
-
- // Group query
- //
-
- groupBases = optionalList(config, "groupBase");
- groupScope = scope(config, "groupScope");
- groupPattern =
- paramString(config, "groupPattern", type.groupPattern());
- final String groupMemberPattern =
- optdef(config, "groupMemberPattern", type.groupMemberPattern());
-
- for (String groupBase : groupBases) {
- if (groupMemberPattern != null) {
- final LdapQuery groupMemberQuery =
- new LdapQuery(groupBase, groupScope, new ParamertizedString(
- groupMemberPattern), Collections.<String> emptySet());
- if (groupMemberQuery.getParameters().isEmpty()) {
- throw new IllegalArgumentException(
- "No variables in ldap.groupMemberPattern");
- }
-
- for (final String name : groupMemberQuery.getParameters()) {
- if (!USERNAME.equals(name)) {
- groupNeedsAccount = true;
- accountAtts.add(name);
- }
- }
-
- groupMemberQueryList.add(groupMemberQuery);
- }
- }
-
- membershipCache =
- new SelfPopulatingCache<String, Set<AccountGroup.Id>>(rawGroup) {
- @Override
- public Set<AccountGroup.Id> createEntry(final String username)
- throws Exception {
- return queryForGroups(username);
- }
-
- @Override
- protected Set<AccountGroup.Id> missing(final String key) {
- return Collections.emptySet();
- }
- };
-
- // Account query
- //
- accountFullName = paramString(config, "accountFullName", type.accountFullName());
- if (accountFullName != null) {
- accountAtts.addAll(accountFullName.getParameterNames());
- }
- accountEmailAddress = paramString(config, "accountEmailAddress", type.accountEmailAddress());
- if (accountEmailAddress != null) {
- accountAtts.addAll(accountEmailAddress.getParameterNames());
- }
- accountSshUserName = paramString(config, "accountSshUserName", type.accountSshUserName());
- if (accountSshUserName != null) {
- accountAtts.addAll(accountSshUserName.getParameterNames());
- }
- accountMemberField = optdef(config, "accountMemberField", type.accountMemberField());
- if (accountMemberField != null) {
- accountAtts.add(accountMemberField);
- }
-
- final SearchScope accountScope = scope(config, "accountScope");
- final String accountPattern =
- reqdef(config, "accountPattern", type.accountPattern());
-
- for (String accountBase : requiredList(config, "accountBase")) {
- final LdapQuery accountQuery =
- new LdapQuery(accountBase, accountScope, new ParamertizedString(
- accountPattern), accountAtts);
- if (accountQuery.getParameters().isEmpty()) {
- throw new IllegalArgumentException(
- "No variables in ldap.accountPattern");
- }
- accountQueryList.add(accountQuery);
- }
-
- usernameCache = new SelfPopulatingCache<String, Account.Id>(rawUsername) {
- @Override
- public Account.Id createEntry(final String username) throws Exception {
- return queryForUsername(username);
- }
- };
- }
-
- private static SearchScope scope(final Config c, final String setting) {
- return ConfigUtil.getEnum(c, "ldap", null, setting, SearchScope.SUBTREE);
- }
-
- private static String optional(final Config config, final String name) {
- return config.getString("ldap", null, name);
- }
-
- private static String required(final Config config, final String name) {
- final String v = optional(config, name);
- if (v == null || "".equals(v)) {
- throw new IllegalArgumentException("No ldap." + name + " configured");
- }
- return v;
- }
-
- private static List<String> optionalList(final Config config,
- final String name) {
- String s[] = config.getStringList("ldap", null, name);
- return Arrays.asList(s);
- }
-
- private static List<String> requiredList(final Config config,
- final String name) {
- List<String> vlist = optionalList(config, name);
-
- if (vlist.isEmpty()) {
- throw new IllegalArgumentException("No ldap " + name + " configured");
- }
-
- return vlist;
- }
-
- private static String optdef(final Config c, final String n, final String d) {
- final String[] v = c.getStringList("ldap", null, n);
- if (v == null || v.length == 0) {
- return d;
-
- } else if (v[0] == null || "".equals(v[0])) {
- return null;
-
- } else {
- return v[0];
- }
- }
-
- private static String reqdef(final Config c, final String n, final String d) {
- final String v = optdef(c, n, d);
- if (v == null) {
- throw new IllegalArgumentException("No ldap." + n + " configured");
- }
- return v;
- }
-
- private static ParamertizedString paramString(Config c, String n, String d) {
- String expression = optdef(c, n, d);
- if (expression == null) {
- return null;
- } else if (expression.contains("${")) {
- return new ParamertizedString(expression);
- } else {
- return new ParamertizedString("${" + expression + "}");
- }
- }
-
- @Override
- public boolean allowsEdit(final Account.FieldName field) {
- switch (field) {
- case FULL_NAME:
- return accountFullName == null; // only if not obtained from LDAP
-
- case SSH_USER_NAME:
- return accountSshUserName == null; // only if not obtained from LDAP
-
- default:
- return true;
- }
- }
-
- private static String apply(ParamertizedString p, LdapQuery.Result m)
- throws NamingException {
- if (p == null) {
- return null;
- }
-
- final Map<String, String> values = new HashMap<String, String>();
- for (final String name : m.attributes()) {
- values.put(name, m.get(name));
- }
-
- String r = p.replace(values);
- return r.isEmpty() ? null : r;
- }
-
- public AuthRequest authenticate(final AuthRequest who)
- throws AccountException {
- final String username = who.getLocalUser();
- try {
- final DirContext ctx = open();
- try {
- final LdapQuery.Result m = findAccount(ctx, username);
-
- if (authConfig.getAuthType() == AuthType.LDAP) {
- // We found the user account, but we need to verify
- // the password matches it before we can continue.
- //
- authenticate(m.getDN(), who.getPassword());
- }
-
- who.setDisplayName(apply(accountFullName, m));
- who.setSshUserName(apply(accountSshUserName, m));
-
- if (accountEmailAddress != null) {
- who.setEmailAddress(apply(accountEmailAddress, m));
-
- } else if (emailExpander.canExpand(username)) {
- // If LDAP cannot give us a valid email address for this user
- // try expanding it through the older email expander code which
- // assumes a user name within a domain.
- //
- who.setEmailAddress(emailExpander.expand(username));
- }
-
- // Fill the cache with the user's current groups. We've already
- // spent the cost to open the LDAP connection, we might as well
- // do one more call to get their group membership. Since we are
- // in the middle of authenticating the user, its likely we will
- // need to know what access rights they have soon.
- //
- membershipCache.put(username, queryForGroups(ctx, username, m));
- return who;
- } finally {
- try {
- ctx.close();
- } catch (NamingException e) {
- log.warn("Cannot close LDAP query handle", e);
- }
- }
- } catch (NamingException e) {
- log.error("Cannot query LDAP to autenticate user", e);
- throw new AccountException("Cannot query LDAP for account", e);
- }
- }
-
- @Override
- public void onCreateAccount(final AuthRequest who, final Account account) {
- usernameCache.put(who.getLocalUser(), account.getId());
- }
-
- @Override
- public Set<AccountGroup.Id> groups(final AccountState who) {
- final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>();
- r.addAll(membershipCache.get(findId(who.getExternalIds())));
- r.addAll(who.getInternalGroups());
- return r;
- }
-
- private Set<AccountGroup.Id> queryForGroups(final String username)
- throws NamingException, AccountException {
- final DirContext ctx = open();
- try {
- return queryForGroups(ctx, username, null);
- } finally {
- try {
- ctx.close();
- } catch (NamingException e) {
- log.warn("Cannot close LDAP query handle", e);
- }
- }
- }
-
- private Set<AccountGroup.Id> queryForGroups(final DirContext ctx,
- final String username, LdapQuery.Result account) throws NamingException,
- AccountException {
- final Set<String> groupDNs = new HashSet<String>();
-
- if (!groupMemberQueryList.isEmpty()) {
- final HashMap<String, String> params = new HashMap<String, String>();
-
- if (groupNeedsAccount) {
- if (account == null) {
- account = findAccount(ctx, username);
- }
- for (final String name : groupMemberQueryList.get(0).getParameters()) {
- params.put(name, account.get(name));
- }
- }
-
- params.put(USERNAME, username);
-
- for (LdapQuery groupMemberQuery : groupMemberQueryList) {
- for (LdapQuery.Result r : groupMemberQuery.query(ctx, params)) {
- groupDNs.add(r.getDN());
- }
- }
- }
-
- if (accountMemberField != null) {
- if (account == null) {
- account = findAccount(ctx, username);
- }
-
- final Attribute groupAtt = account.getAll(accountMemberField);
- if (groupAtt != null) {
- final NamingEnumeration<?> groups = groupAtt.getAll();
- while (groups.hasMore()) {
- recursivelyExpandGroups(groupDNs, ctx, (String) groups.next());
- }
- }
- }
-
- final Set<AccountGroup.Id> actual = new HashSet<AccountGroup.Id>();
- for (String dn : groupDNs) {
- final AccountGroup group;
-
- group = groupCache.get(new AccountGroup.ExternalNameKey(dn));
- if (group != null && group.getType() == AccountGroup.Type.LDAP) {
- actual.add(group.getId());
- }
- }
-
- if (actual.isEmpty()) {
- return Collections.emptySet();
- } else {
- return Collections.unmodifiableSet(actual);
- }
- }
-
- private void recursivelyExpandGroups(final Set<String> groupDNs,
- final DirContext ctx, final String groupDN) {
- if (groupDNs.add(groupDN)) {
- // Recursively identify the groups it is a member of.
- //
- try {
- final Attribute in = ctx.getAttributes(groupDN).get(accountMemberField);
- if (in != null) {
- final NamingEnumeration<?> groups = in.getAll();
- while (groups.hasMore()) {
- recursivelyExpandGroups(groupDNs, ctx, (String) groups.next());
- }
- }
- } catch (NamingException e) {
- log.warn("Could not find group " + groupDN, e);
- }
- }
- }
-
- private static String findId(final Collection<AccountExternalId> ids) {
- for (final AccountExternalId i : ids) {
- if (i.isScheme(AccountExternalId.SCHEME_GERRIT)) {
- return i.getSchemeRest(AccountExternalId.SCHEME_GERRIT);
- }
- }
- return null;
- }
-
- @Override
- public Account.Id lookup(final String accountName) {
- return usernameCache.get(accountName);
- }
-
- @Override
- public Set<AccountGroup.ExternalNameKey> lookupGroups(String name) {
- final Set<AccountGroup.ExternalNameKey> out;
- final ParamertizedString filter =
- ParamertizedString.asis(groupPattern.replace(GROUPNAME, name)
- .toString());
- final Map<String, String> params = Collections.<String, String> emptyMap();
-
- out = new HashSet<AccountGroup.ExternalNameKey>();
- try {
- final DirContext ctx = open();
- try {
- for (String groupBase : groupBases) {
- final LdapQuery query =
- new LdapQuery(groupBase, groupScope, filter, Collections
- .<String> emptySet());
- for (LdapQuery.Result res : query.query(ctx, params)) {
- out.add(new AccountGroup.ExternalNameKey(res.getDN()));
- }
- }
- } finally {
- try {
- ctx.close();
- } catch (NamingException e) {
- log.warn("Cannot close LDAP query handle", e);
- }
- }
- } catch (NamingException e) {
- log.warn("Cannot query LDAP for groups matching requested name", e);
- }
- return out;
- }
-
- private Account.Id queryForUsername(final String username) {
- try {
- final ReviewDb db = schema.open();
- try {
- final String id = AccountExternalId.SCHEME_GERRIT + username;
- final AccountExternalId extId =
- db.accountExternalIds().get(new AccountExternalId.Key(id));
- return extId != null ? extId.getAccountId() : null;
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- log.warn("Cannot query for username in database", e);
- return null;
- }
- }
-
- private Properties createContextProperties() {
- final Properties env = new Properties();
- env.put(Context.INITIAL_CONTEXT_FACTORY, LDAP);
- env.put(Context.PROVIDER_URL, server);
- if (server.startsWith("ldaps:") && !sslVerify) {
- Class<? extends SSLSocketFactory> factory = BlindSSLSocketFactory.class;
- env.put("java.naming.ldap.factory.socket", factory.getName());
- }
- return env;
- }
-
- private DirContext open() throws NamingException {
- final Properties env = createContextProperties();
- if (username != null) {
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put(Context.SECURITY_PRINCIPAL, username);
- env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
- }
- return new InitialDirContext(env);
- }
-
- private void authenticate(String dn, String password) throws AccountException {
- final Properties env = createContextProperties();
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put(Context.SECURITY_PRINCIPAL, dn);
- env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
- try {
- new InitialDirContext(env).close();
- } catch (NamingException e) {
- throw new AccountException("Incorrect username or password", e);
- }
- }
-
- private LdapType discoverLdapType() {
- try {
- final DirContext ctx = open();
- try {
- return LdapType.guessType(ctx);
- } finally {
- ctx.close();
- }
- } catch (NamingException e) {
- log.warn("Cannot discover type of LDAP server at " + server
- + ", assuming the server is RFC 2307 compliant.", e);
- return LdapType.RFC_2307;
- }
- }
-
- private LdapQuery.Result findAccount(final DirContext ctx,
- final String username) throws NamingException, AccountException {
- final HashMap<String, String> params = new HashMap<String, String>();
- params.put(USERNAME, username);
-
- final List<LdapQuery.Result> res = new ArrayList<LdapQuery.Result>();
- for (LdapQuery accountQuery : accountQueryList) {
- res.addAll(accountQuery.query(ctx, params));
- }
-
- switch (res.size()) {
- case 0:
- throw new AccountException("No such user:" + username);
-
- case 1:
- return res.get(0);
-
- default:
- throw new AccountException("Duplicate users: " + username);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LdapType.java b/src/main/java/com/google/gerrit/server/ldap/LdapType.java
deleted file mode 100644
index a0227552cb..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LdapType.java
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.DirContext;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-
-abstract class LdapType {
- static final LdapType RFC_2307 = new Rfc2307();
-
- static LdapType guessType(final DirContext ctx) throws NamingException {
- final SearchControls cons = new SearchControls();
- final NamingEnumeration<SearchResult> res;
-
- final Attributes rootAtts = ctx.getAttributes("");
- Attribute supported = rootAtts.get("supportedCapabilities");
- if (supported != null && supported.contains("1.2.840.113556.1.4.800")) {
- return new ActiveDirectory(rootAtts);
- }
-
- return RFC_2307;
- }
-
- abstract String groupPattern();
-
- abstract String groupMemberPattern();
-
- abstract String accountFullName();
-
- abstract String accountEmailAddress();
-
- abstract String accountSshUserName();
-
- abstract String accountMemberField();
-
- abstract String accountPattern();
-
- private static class Rfc2307 extends LdapType {
- @Override
- String groupPattern() {
- return "(cn=${groupname})";
- }
-
- @Override
- String groupMemberPattern() {
- return "(memberUid=${username})";
- }
-
- @Override
- String accountFullName() {
- return "displayName";
- }
-
- @Override
- String accountEmailAddress() {
- return "mail";
- }
-
- @Override
- String accountSshUserName() {
- return "uid";
- }
-
- @Override
- String accountMemberField() {
- return null; // Not defined in RFC 2307
- }
-
- @Override
- String accountPattern() {
- return "(uid=${username})";
- }
- }
-
- private static class ActiveDirectory extends LdapType {
- private final String defaultDomain;
-
- ActiveDirectory(final Attributes atts) throws NamingException {
- // Convert "defaultNamingContext: DC=foo,DC=example,DC=com" into
- // the a standard DNS name as we would expect to find in the suffix
- // part of the userPrincipalName.
- //
- Attribute defaultNamingContext = atts.get("defaultNamingContext");
- if (defaultNamingContext == null || defaultNamingContext.size() < 1) {
- throw new NamingException("rootDSE has no defaultNamingContext");
- }
-
- final StringBuilder b = new StringBuilder();
- for (String n : ((String) defaultNamingContext.get(0)).split(", *")) {
- if (n.toUpperCase().startsWith("DC=")) {
- if (b.length() > 0) {
- b.append('.');
- }
- b.append(n.substring(3));
- }
- }
- defaultDomain = b.toString();
- }
-
- @Override
- String groupPattern() {
- return "(&(objectClass=group)(cn=${groupname}))";
- }
-
- @Override
- String groupMemberPattern() {
- return null; // Active Directory uses memberOf in the account
- }
-
- @Override
- String accountFullName() {
- return "${givenName} ${sn}";
- }
-
- @Override
- String accountEmailAddress() {
- return "mail";
- }
-
- @Override
- String accountSshUserName() {
- return "${sAMAccountName.toLowerCase}";
- }
-
- @Override
- String accountMemberField() {
- return "memberOf";
- }
-
- @Override
- String accountPattern() {
- return "(&(objectClass=user)(sAMAccountName=${username}))";
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/LoginRedirectServlet.java b/src/main/java/com/google/gerrit/server/ldap/LoginRedirectServlet.java
deleted file mode 100644
index 62366d04f4..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/LoginRedirectServlet.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.SignInDialog;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.http.WebSession;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@Singleton
-class LoginRedirectServlet extends HttpServlet {
- private final Provider<WebSession> webSession;
- private final Provider<String> urlProvider;
-
- @Inject
- LoginRedirectServlet(final Provider<WebSession> webSession,
- @CanonicalWebUrl @Nullable final Provider<String> urlProvider) {
- this.webSession = webSession;
- this.urlProvider = urlProvider;
- }
-
- @Override
- protected void doGet(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- final String token;
- if (webSession.get().isSignedIn()) {
- token = getToken(req);
- } else {
- final String msg = "Session cookie not available.";
- token = "SignInFailure," + SignInDialog.Mode.SIGN_IN + "," + msg;
- }
-
- final StringBuilder rdr = new StringBuilder();
- rdr.append(urlProvider.get());
- rdr.append('#');
- rdr.append(token);
-
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- rsp.sendRedirect(rdr.toString());
- }
-
- private String getToken(final HttpServletRequest req) {
- String token = req.getPathInfo();
- if (token != null && token.startsWith("/")) {
- token = token.substring(1);
- }
- if (token == null || token.isEmpty()) {
- token = Link.MINE;
- }
- return token;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/SearchScope.java b/src/main/java/com/google/gerrit/server/ldap/SearchScope.java
deleted file mode 100644
index dcc6089d81..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/SearchScope.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import javax.naming.directory.SearchControls;
-
-public enum SearchScope {
- // Search only the base DN
- //
- OBJECT(SearchControls.OBJECT_SCOPE), //
- BASE(SearchControls.OBJECT_SCOPE),
-
- // Search all entries one level under the base DN
- //
- // Does not include the base DN, and does not include items below items
- // under the base DN.
- //
- ONE(SearchControls.ONELEVEL_SCOPE),
-
- // Search all entries under the base DN, including the base DN.
- //
- SUBTREE(SearchControls.SUBTREE_SCOPE), //
- SUB(SearchControls.SUBTREE_SCOPE);
-
- private final int scope;
-
- SearchScope(final int scope) {
- this.scope = scope;
- }
-
- int scope() {
- return scope;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ldap/UserPassAuthServiceImpl.java b/src/main/java/com/google/gerrit/server/ldap/UserPassAuthServiceImpl.java
deleted file mode 100644
index 80da01110b..0000000000
--- a/src/main/java/com/google/gerrit/server/ldap/UserPassAuthServiceImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ldap;
-
-import com.google.gerrit.client.auth.userpass.UserPassAuthService;
-import com.google.gerrit.client.auth.userpass.LoginResult;
-import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.AuthRequest;
-import com.google.gerrit.server.account.AuthResult;
-import com.google.gerrit.server.http.WebSession;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-class UserPassAuthServiceImpl implements UserPassAuthService {
- private final Provider<WebSession> webSession;
- private final AccountManager accountManager;
-
- @Inject
- UserPassAuthServiceImpl(final Provider<WebSession> webSession,
- final AccountManager accountManager) {
- this.webSession = webSession;
- this.accountManager = accountManager;
- }
-
- @Override
- public void authenticate(final String username, final String password,
- final AsyncCallback<LoginResult> callback) {
- LoginResult result = new LoginResult();
- if (username == null || "".equals(username) //
- || password == null || "".equals(password)) {
- result.success = false;
- callback.onSuccess(result);
- return;
- }
-
- final AuthRequest req = AuthRequest.forUser(username);
- req.setPassword(password);
-
- final AuthResult res;
- try {
- res = accountManager.authenticate(req);
- } catch (AccountException e) {
- result.success = false;
- callback.onSuccess(result);
- return;
- }
-
- result.success = true;
- result.isNew = res.isNew();
- webSession.get().login(res.getAccountId(), false);
- callback.onSuccess(result);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java b/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
deleted file mode 100644
index e771ac725c..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/AbandonedSender.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-/** Send notice about a change being abandoned by its owner. */
-public class AbandonedSender extends ReplyToChangeSender {
- public static interface Factory {
- AbandonedSender create(Change change);
- }
-
- @Inject
- public AbandonedSender(@Assisted Change c) {
- super(c, "abandon");
- }
-
- @Override
- protected void init() {
- super.init();
-
- ccAllApprovals();
- bccStarredBy();
- bccWatchesNotifyAllComments();
- }
-
- @Override
- protected void format() {
- appendText(getNameFor(fromId));
- appendText(" has abandoned change " + change.getKey().abbreviate() + ":\n");
- appendText("\n");
- formatCoverLetter();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java b/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java
deleted file mode 100644
index 7c9e507e2c..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/AddReviewerSender.java
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-/** Asks a user to review a change. */
-public class AddReviewerSender extends NewChangeSender {
- public static interface Factory {
- AddReviewerSender create(Change change);
- }
-
- @Inject
- public AddReviewerSender(@Assisted Change c) {
- super(c);
- }
-
- @Override
- protected void init() {
- super.init();
-
- ccExistingReviewers();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/src/main/java/com/google/gerrit/server/mail/CommentSender.java
deleted file mode 100644
index 7f52fac618..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.server.patch.PatchFile;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.List;
-
-/** Send comments, after the author of them hit used Publish Comments in the UI. */
-public class CommentSender extends ReplyToChangeSender {
- public static interface Factory {
- public CommentSender create(Change change);
- }
-
- private List<PatchLineComment> inlineComments = Collections.emptyList();
-
- @Inject
- public CommentSender(@Assisted Change c) {
- super(c, "comment");
- }
-
- public void setPatchLineComments(final List<PatchLineComment> plc) {
- inlineComments = plc;
- }
-
- @Override
- protected void init() {
- super.init();
-
- ccAllApprovals();
- bccStarredBy();
- bccWatchesNotifyAllComments();
- }
-
- @Override
- protected void format() {
- if (!"".equals(getCoverLetter()) || !inlineComments.isEmpty()) {
- appendText("Comments on Patch Set " + patchSet.getPatchSetId() + ":\n");
- appendText("\n");
- formatCoverLetter();
- formatInlineComments();
- if (getChangeUrl() != null) {
- appendText("To respond, visit " + getChangeUrl() + "\n");
- appendText("\n");
- }
- }
- }
-
- private void formatInlineComments() {
- final Repository repo = getRepository();
- try {
- final PatchList patchList = repo != null ? getPatchList() : null;
-
- Patch.Key currentFileKey = null;
- PatchFile currentFileData = null;
- for (final PatchLineComment c : inlineComments) {
- final Patch.Key pk = c.getKey().getParentKey();
- final int lineNbr = c.getLine();
- final short side = c.getSide();
-
- if (!pk.equals(currentFileKey)) {
- appendText("....................................................\n");
- appendText("File ");
- appendText(pk.get());
- appendText("\n");
- currentFileKey = pk;
-
- if (patchList != null) {
- try {
- currentFileData =
- new PatchFile(repo, patchList, pk.getFileName());
- } catch (IOException e) {
- // Don't quote the line if we can't load it.
- }
- } else {
- currentFileData = null;
- }
- }
-
- appendText("Line " + lineNbr);
- if (currentFileData != null) {
- try {
- final String lineStr = currentFileData.getLine(side, lineNbr);
- appendText(": ");
- appendText(lineStr);
- } catch (Throwable cce) {
- // Don't quote the line if we can't safely convert it.
- }
- }
- appendText("\n");
-
- appendText(c.getMessage().trim());
- appendText("\n\n");
- }
- } finally {
- if (repo != null) {
- repo.close();
- }
- }
- }
-
- private Repository getRepository() {
- try {
- return server.openRepository(projectName);
- } catch (RepositoryNotFoundException e) {
- return null;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java b/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
deleted file mode 100644
index 1c0bee7596..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/** Notify interested parties of a brand new change. */
-public class CreateChangeSender extends NewChangeSender {
- public static interface Factory {
- public CreateChangeSender create(Change change);
- }
-
- @Inject
- public CreateChangeSender(@Assisted Change c) {
- super(c);
- }
-
- @Override
- protected void init() {
- super.init();
-
- bccWatchers();
- }
-
- private void bccWatchers() {
- if (db != null) {
- try {
- // BCC anyone else who has interest in this project's changes
- //
- final ProjectState ps = getProjectState();
- if (ps != null) {
- // Try to mark interested owners with a TO and not a BCC line.
- //
- final Set<Account.Id> owners = new HashSet<Account.Id>();
- for (AccountGroup.Id g : getProjectOwners()) {
- for (AccountGroupMember m : db.accountGroupMembers().byGroup(g)) {
- owners.add(m.getAccountId());
- }
- }
-
- // BCC anyone who has interest in this project's changes
- //
- for (AccountProjectWatch w : db.accountProjectWatches()
- .notifyNewChanges(ps.getProject().getNameKey())) {
- if (owners.contains(w.getAccountId())) {
- add(RecipientType.TO, w.getAccountId());
- } else {
- add(RecipientType.BCC, w.getAccountId());
- }
- }
- }
- } catch (OrmException err) {
- // Just don't CC everyone. Better to send a partial message to those
- // we already have queued up then to fail deliver entirely to people
- // who have a lower interest in the change.
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java b/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java
deleted file mode 100644
index 512e5e8e75..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/FromAddressGenerator.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-
-/** Constructs an address to send email from. */
-public interface FromAddressGenerator {
- public Address from(Account.Id fromId);
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java b/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java
deleted file mode 100644
index f9322d44b7..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/FromAddressGeneratorProvider.java
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.ParamertizedString;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-
-/** Creates a {@link FromAddressGenerator} from the {@link GerritServerConfig} */
-@Singleton
-public class FromAddressGeneratorProvider implements
- Provider<FromAddressGenerator> {
- private final FromAddressGenerator generator;
-
- @Inject
- FromAddressGeneratorProvider(@GerritServerConfig final Config cfg,
- @GerritPersonIdent final PersonIdent myIdent,
- final AccountCache accountCache) {
-
- final String from = cfg.getString("sendemail", null, "from");
- final Address srvAddr = toAddress(myIdent);
-
- if (from == null || "MIXED".equalsIgnoreCase(from)) {
- final String name = "${user} (Code Review)";
- final String email = srvAddr.email;
- generator = new PatternGen(srvAddr, accountCache, name, email);
-
- } else if ("USER".equalsIgnoreCase(from)) {
- generator = new UserGen(accountCache, srvAddr);
-
- } else if ("SERVER".equalsIgnoreCase(from)) {
- generator = new ServerGen(srvAddr);
-
- } else {
- final Address a = Address.parse(from);
- generator = new PatternGen(srvAddr, accountCache, a.name, a.email);
- }
- }
-
- private static Address toAddress(final PersonIdent myIdent) {
- return new Address(myIdent.getName(), myIdent.getEmailAddress());
- }
-
- @Override
- public FromAddressGenerator get() {
- return generator;
- }
-
- static final class UserGen implements FromAddressGenerator {
- private final AccountCache accountCache;
- private final Address srvAddr;
-
- UserGen(AccountCache accountCache, Address srvAddr) {
- this.accountCache = accountCache;
- this.srvAddr = srvAddr;
- }
-
- @Override
- public Address from(final Account.Id fromId) {
- if (fromId != null) {
- final Account a = accountCache.get(fromId).getAccount();
- if (a.getPreferredEmail() != null) {
- return new Address(a.getFullName(), a.getPreferredEmail());
- }
- }
- return srvAddr;
- }
- }
-
- static final class ServerGen implements FromAddressGenerator {
- private final Address srvAddr;
-
- ServerGen(Address srvAddr) {
- this.srvAddr = srvAddr;
- }
-
- @Override
- public Address from(final Account.Id fromId) {
- return srvAddr;
- }
- }
-
- static final class PatternGen implements FromAddressGenerator {
- private final String senderEmail;
- private final Address serverAddress;
- private final AccountCache accountCache;
- private final ParamertizedString namePattern;
-
- PatternGen(final Address serverAddress, final AccountCache accountCache,
- final String namePattern, final String senderEmail) {
- this.senderEmail = senderEmail;
- this.serverAddress = serverAddress;
- this.accountCache = accountCache;
- this.namePattern = new ParamertizedString(namePattern);
- }
-
- @Override
- public Address from(final Account.Id fromId) {
- final String senderName;
-
- if (fromId != null) {
- final Account account = accountCache.get(fromId).getAccount();
- String fullName = account.getFullName();
- if (fullName == null || "".equals(fullName)) {
- fullName = "Anonymous Coward";
- }
- senderName = namePattern.replace("user", fullName).toString();
-
- } else {
- senderName = serverAddress.name;
- }
-
- return new Address(senderName, senderEmail);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java b/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java
deleted file mode 100644
index c587d5c8db..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/MergeFailSender.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-/** Send notice about a change failing to merged. */
-public class MergeFailSender extends ReplyToChangeSender {
- public static interface Factory {
- public MergeFailSender create(Change change);
- }
-
- @Inject
- public MergeFailSender(@Assisted Change c) {
- super(c, "comment");
- }
-
- @Override
- protected void init() {
- super.init();
-
- ccExistingReviewers();
- }
-
- @Override
- protected void format() {
- appendText("Change " + change.getKey().abbreviate());
- if (patchSetInfo != null && patchSetInfo.getAuthor() != null
- && patchSetInfo.getAuthor().getName() != null) {
- appendText(" by ");
- appendText(patchSetInfo.getAuthor().getName());
- }
- appendText(" FAILED to submit to ");
- appendText(change.getDest().getShortName());
- appendText(".\n\n");
- formatCoverLetter();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/MergedSender.java b/src/main/java/com/google/gerrit/server/mail/MergedSender.java
deleted file mode 100644
index e49eaa0b70..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/MergedSender.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** Send notice about a change successfully merged. */
-public class MergedSender extends ReplyToChangeSender {
- public static interface Factory {
- public MergedSender create(Change change);
- }
-
- private Branch.NameKey dest;
-
- @Inject
- private ApprovalTypes approvalTypes;
-
- @Inject
- public MergedSender(@Assisted Change c) {
- super(c, "merged");
- dest = c.getDest();
- }
-
- public void setDest(final Branch.NameKey key) {
- dest = key;
- }
-
- @Override
- protected void init() {
- super.init();
-
- ccAllApprovals();
- bccStarredBy();
- bccWatchesNotifyAllComments();
- bccWatchesNotifySubmittedChanges();
- }
-
- @Override
- protected void format() {
- appendText("Change " + change.getKey().abbreviate());
- if (patchSetInfo != null && patchSetInfo.getAuthor() != null
- && patchSetInfo.getAuthor().getName() != null) {
- appendText(" by ");
- appendText(patchSetInfo.getAuthor().getName());
- }
- appendText(" submitted to ");
- appendText(dest.getShortName());
- appendText(":\n\n");
- formatChangeDetail();
- formatApprovals();
- }
-
- private void formatApprovals() {
- if (db != null && patchSet != null) {
- try {
- final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> pos =
- new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>();
-
- final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> neg =
- new HashMap<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>>();
-
- for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(
- patchSet.getId())) {
- if (ca.getValue() > 0) {
- insert(pos, ca);
- } else if (ca.getValue() < 0) {
- insert(neg, ca);
- }
- }
-
- format("Approvals", pos);
- format("Objections", neg);
- } catch (OrmException err) {
- // Don't list the approvals
- }
- }
- }
-
- private void format(final String type,
- final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list) {
- if (list.isEmpty()) {
- return;
- }
- appendText(type + ":\n");
- for (final Map.Entry<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> ent : list
- .entrySet()) {
- final Map<ApprovalCategory.Id, PatchSetApproval> l = ent.getValue();
- appendText(" ");
- appendText(getNameFor(ent.getKey()));
- appendText(": ");
- boolean first = true;
- for (ApprovalType at : approvalTypes.getApprovalTypes()) {
- final PatchSetApproval ca = l.get(at.getCategory().getId());
- if (ca == null) {
- continue;
- }
-
- if (first) {
- first = false;
- } else {
- appendText("; ");
- }
-
- final ApprovalCategoryValue v = at.getValue(ca);
- if (v != null) {
- appendText(v.getName());
- } else {
- appendText(at.getCategory().getName());
- appendText("=");
- if (ca.getValue() > 0) {
- appendText("+");
- }
- appendText("" + ca.getValue());
- }
- }
- appendText("\n");
- }
- appendText("\n");
- }
-
- private void insert(
- final Map<Account.Id, Map<ApprovalCategory.Id, PatchSetApproval>> list,
- final PatchSetApproval ca) {
- Map<ApprovalCategory.Id, PatchSetApproval> m = list.get(ca.getAccountId());
- if (m == null) {
- m = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- list.put(ca.getAccountId(), m);
- }
- m.put(ca.getCategoryId(), ca);
- }
-
- private void bccWatchesNotifySubmittedChanges() {
- if (db != null) {
- try {
- // BCC anyone else who has interest in this project's changes
- //
- final ProjectState ps = getProjectState();
- if (ps != null) {
- for (AccountProjectWatch w : db.accountProjectWatches()
- .notifySubmittedChanges(ps.getProject().getNameKey())) {
- add(RecipientType.BCC, w.getAccountId());
- }
- }
- } catch (OrmException err) {
- // Just don't CC everyone. Better to send a partial message to those
- // we already have queued up then to fail deliver entirely to people
- // who have a lower interest in the change.
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java b/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
deleted file mode 100644
index 9a0f41e6cf..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/NewChangeSender.java
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.inject.Inject;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/** Sends an email alerting a user to a new change for them to review. */
-public abstract class NewChangeSender extends OutgoingEmail {
- @Inject
- private SshInfo sshInfo;
-
- private final Set<Account.Id> reviewers = new HashSet<Account.Id>();
- private final Set<Account.Id> extraCC = new HashSet<Account.Id>();
-
- protected NewChangeSender(Change c) {
- super(c, "newchange");
- }
-
- public void addReviewers(final Collection<Account.Id> cc) {
- reviewers.addAll(cc);
- }
-
- public void addExtraCC(final Collection<Account.Id> cc) {
- extraCC.addAll(cc);
- }
-
- @Override
- protected void init() {
- super.init();
-
- setHeader("Message-ID", getChangeMessageThreadId());
-
- add(RecipientType.TO, reviewers);
- add(RecipientType.CC, extraCC);
- rcptToAuthors(RecipientType.CC);
- }
-
- @Override
- protected void format() {
- formatSalutation();
- formatChangeDetail();
-
- appendText("\n");
- appendText(" " + getPullUrl() + "\n");
- }
-
- private void formatSalutation() {
- final String changeUrl = getChangeUrl();
-
- if (reviewers.isEmpty()) {
- formatDest();
- if (changeUrl != null) {
- appendText("\n");
- appendText(" " + changeUrl + "\n");
- appendText("\n");
- }
- appendText("\n");
-
- } else {
- appendText("Hello");
- for (final Iterator<Account.Id> i = reviewers.iterator(); i.hasNext();) {
- appendText(" ");
- appendText(getNameFor(i.next()));
- appendText(",");
- }
- appendText("\n");
- appendText("\n");
-
- appendText("I'd like you to do a code review.");
- if (changeUrl != null) {
- appendText(" Please visit\n");
- appendText("\n");
- appendText(" " + changeUrl + "\n");
- appendText("\n");
- appendText("to review the following change:\n");
- }
- appendText("\n");
-
- formatDest();
- appendText("\n");
- }
- }
-
- private void formatDest() {
- appendText("Change " + change.getKey().abbreviate());
- appendText(" for ");
- appendText(change.getDest().getShortName());
- appendText(" in ");
- appendText(projectName);
- appendText(":\n");
- }
-
- private String getPullUrl() {
- final StringBuilder r = new StringBuilder();
- r.append("git pull ssh://");
- String sshAddress = sshInfo.getSshdAddress();
- if (sshAddress.startsWith(":") || "".equals(sshAddress)) {
- r.append(getGerritHost());
- }
- r.append(sshAddress);
- r.append("/");
- r.append(projectName);
- r.append(" ");
- r.append(patchSet.getRefName());
- return r.toString();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java b/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
deleted file mode 100644
index f54084f9ed..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/OutgoingEmail.java
+++ /dev/null
@@ -1,632 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.StarredChange;
-import com.google.gerrit.client.reviewdb.UserIdentity;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListEntry;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.util.SystemReader;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
-/** Sends an email to one or more interested parties. */
-public abstract class OutgoingEmail {
- private static final String HDR_TO = "To";
- private static final String HDR_CC = "CC";
-
- private static final Random RNG = new Random();
- private final String messageClass;
- protected final Change change;
- protected String projectName;
- private final HashSet<Account.Id> rcptTo = new HashSet<Account.Id>();
- private final Map<String, EmailHeader> headers;
- private final List<Address> smtpRcptTo = new ArrayList<Address>();
- private Address smtpFromAddress;
- private StringBuilder body;
- private boolean inFooter;
-
- protected Account.Id fromId;
- protected PatchSet patchSet;
- protected PatchSetInfo patchSetInfo;
- protected ChangeMessage changeMessage;
- protected ReviewDb db;
-
- @Inject
- protected GitRepositoryManager server;
-
- @Inject
- private ProjectCache projectCache;
-
- @Inject
- private AccountCache accountCache;
-
- @Inject
- private PatchListCache patchListCache;
-
- @Inject
- private FromAddressGenerator fromAddressGenerator;
-
- @Inject
- private EmailSender emailSender;
-
- @Inject
- private PatchSetInfoFactory patchSetInfoFactory;
-
- @Inject
- private IdentifiedUser.GenericFactory identifiedUserFactory;
-
- @Inject
- @CanonicalWebUrl
- @Nullable
- private Provider<String> urlProvider;
-
- private ProjectState projectState;
-
- protected OutgoingEmail(final Change c, final String mc) {
- change = c;
- messageClass = mc;
- headers = new LinkedHashMap<String, EmailHeader>();
- }
-
- protected OutgoingEmail(final String mc) {
- this(null, mc);
- }
-
- public void setFrom(final Account.Id id) {
- fromId = id;
- }
-
- public void setPatchSet(final PatchSet ps) {
- patchSet = ps;
- }
-
- public void setPatchSet(final PatchSet ps, final PatchSetInfo psi) {
- patchSet = ps;
- patchSetInfo = psi;
- }
-
- public void setChangeMessage(final ChangeMessage cm) {
- changeMessage = cm;
- }
-
- public void setReviewDb(final ReviewDb d) {
- db = d;
- }
-
- /**
- * Format and enqueue the message for delivery.
- *
- * @throws EmailException
- */
- public void send() throws EmailException {
- if (!emailSender.isEnabled()) {
- // Server has explicitly disabled email sending.
- //
- return;
- }
-
- init();
- format();
- if (shouldSendMessage()) {
- if (fromId != null) {
- // If we are impersonating a user, make sure they receive a CC of
- // this message so they can always review and audit what we sent
- // on their behalf to others.
- //
- add(RecipientType.CC, fromId);
- }
- if (change != null) {
- if (getChangeUrl() != null) {
- openFooter();
- appendText("To view visit ");
- appendText(getChangeUrl());
- appendText("\n");
- }
- if (getSettingsUrl() != null) {
- openFooter();
- appendText("To unsubscribe, visit ");
- appendText(getSettingsUrl());
- appendText("\n");
- }
-
- if (inFooter) {
- appendText("\n");
- } else {
- openFooter();
- }
- appendText("Gerrit-MessageType: " + messageClass + "\n");
- appendText("Gerrit-Project: " + projectName + "\n");
- appendText("Gerrit-Branch: " + change.getDest().getShortName() + "\n");
- }
-
- if (headers.get("Message-ID").isEmpty()) {
- final StringBuilder rndid = new StringBuilder();
- rndid.append("<");
- rndid.append(System.currentTimeMillis());
- rndid.append("-");
- rndid.append(Integer.toString(RNG.nextInt(999999), 36));
- rndid.append("@");
- rndid.append(SystemReader.getInstance().getHostname());
- rndid.append(">");
- setHeader("Message-ID", rndid.toString());
- }
-
- emailSender.send(smtpFromAddress, smtpRcptTo, headers, body.toString());
- }
- }
-
- /** Format the message body by calling {@link #appendText(String)}. */
- protected abstract void format();
-
- /** Setup the message headers and envelope (TO, CC, BCC). */
- protected void init() {
- if (change != null && projectCache != null) {
- projectState = projectCache.get(change.getProject());
- projectName =
- projectState != null ? projectState.getProject().getName() : null;
- } else {
- projectState = null;
- projectName = null;
- }
-
- smtpFromAddress = fromAddressGenerator.from(fromId);
- if (changeMessage != null && changeMessage.getWrittenOn() != null) {
- setHeader("Date", new Date(changeMessage.getWrittenOn().getTime()));
- } else {
- setHeader("Date", new Date());
- }
- headers.put("From", new EmailHeader.AddressList(smtpFromAddress));
- headers.put(HDR_TO, new EmailHeader.AddressList());
- headers.put(HDR_CC, new EmailHeader.AddressList());
- if (change != null) {
- setChangeSubjectHeader();
- }
- setHeader("Message-ID", "");
-
- if (fromId != null) {
- // If we have a user that this message is supposedly caused by
- // but the From header on the email does not match the user as
- // it is a generic header for this Gerrit server, include the
- // Reply-To header with the current user's email address.
- //
- final Address a = toAddress(fromId);
- if (a != null && !smtpFromAddress.email.equals(a.email)) {
- setHeader("Reply-To", a.email);
- }
- }
-
- setHeader("X-Gerrit-MessageType", messageClass);
- if (change != null) {
- setHeader("X-Gerrit-Change-Id", "" + change.getKey().get());
- setListIdHeader();
- setChangeUrlHeader();
- setCommitIdHeader();
- }
- body = new StringBuilder();
- inFooter = false;
-
- if (change != null && db != null) {
- if (patchSet == null) {
- try {
- patchSet = db.patchSets().get(change.currentPatchSetId());
- } catch (OrmException err) {
- patchSet = null;
- }
- }
-
- if (patchSet != null && patchSetInfo == null) {
- try {
- patchSetInfo = patchSetInfoFactory.get(patchSet.getId());
- } catch (PatchSetInfoNotAvailableException err) {
- patchSetInfo = null;
- }
- }
- }
- }
-
- private void setListIdHeader() {
- // Set a reasonable list id so that filters can be used to sort messages
- //
- final StringBuilder listid = new StringBuilder();
- listid.append("gerrit-");
- listid.append(projectName.replace('/', '-'));
- listid.append("@");
- listid.append(getGerritHost());
-
- final String listidStr = listid.toString();
- setHeader("Mailing-List", "list " + listidStr);
- setHeader("List-Id", "<" + listidStr.replace('@', '.') + ">");
- if (getSettingsUrl() != null) {
- setHeader("List-Unsubscribe", "<" + getSettingsUrl() + ">");
- }
- }
-
- private void setChangeUrlHeader() {
- final String u = getChangeUrl();
- if (u != null) {
- setHeader("X-Gerrit-ChangeURL", "<" + u + ">");
- }
- }
-
- private void setCommitIdHeader() {
- if (patchSet != null && patchSet.getRevision() != null
- && patchSet.getRevision().get() != null
- && patchSet.getRevision().get().length() > 0) {
- setHeader("X-Gerrit-Commit", patchSet.getRevision().get());
- }
- }
-
- private void setChangeSubjectHeader() {
- final StringBuilder subj = new StringBuilder();
- subj.append("[");
- subj.append(change.getDest().getShortName());
- subj.append("] ");
- subj.append("Change ");
- subj.append(change.getKey().abbreviate());
- subj.append(": (");
- subj.append(projectName);
- subj.append(") ");
- if (change.getSubject().length() > 60) {
- subj.append(change.getSubject().substring(0, 60));
- subj.append("...");
- } else {
- subj.append(change.getSubject());
- }
- setHeader("Subject", subj.toString());
- }
-
- protected String getGerritHost() {
- if (getGerritUrl() != null) {
- try {
- return new URL(getGerritUrl()).getHost();
- } catch (MalformedURLException e) {
- // Try something else.
- }
- }
-
- // Fall back onto whatever the local operating system thinks
- // this server is called. We hopefully didn't get here as a
- // good admin would have configured the canonical url.
- //
- return SystemReader.getInstance().getHostname();
- }
-
- /** Get a link to the change; null if the server doesn't know its own address. */
- protected String getChangeUrl() {
- if (change != null && getGerritUrl() != null) {
- final StringBuilder r = new StringBuilder();
- r.append(getGerritUrl());
- r.append(change.getChangeId());
- return r.toString();
- }
- return null;
- }
-
- private String getSettingsUrl() {
- if (getGerritUrl() != null) {
- final StringBuilder r = new StringBuilder();
- r.append(getGerritUrl());
- r.append("settings");
- return r.toString();
- }
- return null;
- }
-
- protected String getGerritUrl() {
- return urlProvider.get();
- }
-
- protected String getChangeMessageThreadId() {
- final StringBuilder r = new StringBuilder();
- r.append('<');
- r.append("gerrit");
- r.append('.');
- r.append(change.getCreatedOn().getTime());
- r.append('.');
- r.append(change.getKey().get());
- r.append('@');
- r.append(getGerritHost());
- r.append('>');
- return r.toString();
- }
-
- /** Set a header in the outgoing message. */
- protected void setHeader(final String name, final String value) {
- headers.put(name, new EmailHeader.String(value));
- }
-
- protected void setHeader(final String name, final Date date) {
- headers.put(name, new EmailHeader.Date(date));
- }
-
- /** Append text to the outgoing email body. */
- protected void appendText(final String text) {
- if (text != null) {
- body.append(text);
- }
- }
-
- private void openFooter() {
- if (!inFooter) {
- inFooter = true;
- appendText("-- \n");
- }
- }
-
- /** Format the sender's "cover letter", {@link #getCoverLetter()}. */
- protected void formatCoverLetter() {
- final String cover = getCoverLetter();
- if (!"".equals(cover)) {
- appendText(cover);
- appendText("\n\n");
- }
- }
-
- /** Get the text of the "cover letter", from {@link ChangeMessage}. */
- protected String getCoverLetter() {
- if (changeMessage != null) {
- final String txt = changeMessage.getMessage();
- if (txt != null) {
- return txt.trim();
- }
- }
- return "";
- }
-
- /** Format the change message and the affected file list. */
- protected void formatChangeDetail() {
- if (patchSetInfo != null) {
- appendText(patchSetInfo.getMessage().trim());
- appendText("\n");
- } else {
- appendText(change.getSubject().trim());
- appendText("\n");
- }
-
- if (patchSet != null) {
- appendText("---\n");
- for (PatchListEntry p : getPatchList().getPatches()) {
- appendText(p.getChangeType().getCode() + " " + p.getNewName() + "\n");
- }
- appendText("\n");
- }
- }
-
- /** Get the patch list corresponding to this patch set. */
- protected PatchList getPatchList() {
- if (patchSet != null) {
- return patchListCache.get(change, patchSet);
- }
- return null;
- }
-
- /** Lookup a human readable name for an account, usually the "full name". */
- protected String getNameFor(final Account.Id accountId) {
- if (accountId == null) {
- return "Anonymous Coward";
- }
-
- final Account userAccount = accountCache.get(accountId).getAccount();
- String name = userAccount.getFullName();
- if (name == null) {
- name = userAccount.getPreferredEmail();
- }
- if (name == null) {
- name = "Anonymous Coward #" + accountId;
- }
- return name;
- }
-
- protected boolean shouldSendMessage() {
- if (body.length() == 0) {
- // If we have no message body, don't send.
- //
- return false;
- }
-
- if (rcptTo.isEmpty()) {
- // If we have nobody to send this message to, then all of our
- // selection filters previously for this type of message were
- // unable to match a destination. Don't bother sending it.
- //
- return false;
- }
-
- if (rcptTo.size() == 1 && rcptTo.contains(fromId)) {
- // If the only recipient is also the sender, don't bother.
- //
- return false;
- }
-
- return true;
- }
-
- /** Get the project entity the change is in; null if its been deleted. */
- protected ProjectState getProjectState() {
- return projectState;
- }
-
- /** Get the groups which own the project. */
- protected Set<AccountGroup.Id> getProjectOwners() {
- final ProjectState r;
-
- r = projectCache.get(change.getProject());
- return r != null ? r.getOwners() : Collections.<AccountGroup.Id> emptySet();
- }
-
- /** Schedule this message for delivery to the listed accounts. */
- protected void add(final RecipientType rt, final Collection<Account.Id> list) {
- for (final Account.Id id : list) {
- add(rt, id);
- }
- }
-
- /** TO or CC all vested parties (change owner, patch set uploader, author). */
- protected void rcptToAuthors(final RecipientType rt) {
- add(rt, change.getOwner());
- if (patchSet != null) {
- add(rt, patchSet.getUploader());
- }
- if (patchSetInfo != null) {
- add(rt, patchSetInfo.getAuthor());
- add(rt, patchSetInfo.getCommitter());
- }
- }
-
- private void add(final RecipientType rt, final UserIdentity who) {
- if (who != null && who.getAccount() != null) {
- add(rt, who.getAccount());
- }
- }
-
- /** BCC any user who has starred this change. */
- protected void bccStarredBy() {
- if (db != null) {
- try {
- // BCC anyone who has starred this change.
- //
- for (StarredChange w : db.starredChanges().byChange(change.getId())) {
- add(RecipientType.BCC, w.getAccountId());
- }
- } catch (OrmException err) {
- // Just don't BCC everyone. Better to send a partial message to those
- // we already have queued up then to fail deliver entirely to people
- // who have a lower interest in the change.
- }
- }
- }
-
- /** BCC any user who has set "notify all comments" on this project. */
- protected void bccWatchesNotifyAllComments() {
- if (db != null) {
- try {
- // BCC anyone else who has interest in this project's changes
- //
- final ProjectState ps = getProjectState();
- if (ps != null) {
- for (final AccountProjectWatch w : db.accountProjectWatches()
- .notifyAllComments(ps.getProject().getNameKey())) {
- add(RecipientType.BCC, w.getAccountId());
- }
- }
- } catch (OrmException err) {
- // Just don't CC everyone. Better to send a partial message to those
- // we already have queued up then to fail deliver entirely to people
- // who have a lower interest in the change.
- }
- }
- }
-
- /** Any user who has published comments on this change. */
- protected void ccAllApprovals() {
- ccApprovals(true);
- }
-
- /** Users who have non-zero approval codes on the change. */
- protected void ccExistingReviewers() {
- ccApprovals(false);
- }
-
- private void ccApprovals(final boolean includeZero) {
- if (db != null) {
- try {
- // CC anyone else who has posted an approval mark on this change
- //
- for (PatchSetApproval ap : db.patchSetApprovals().byChange(
- change.getId())) {
- if (!includeZero && ap.getValue() == 0) {
- continue;
- }
- add(RecipientType.CC, ap.getAccountId());
- }
- } catch (OrmException err) {
- }
- }
- }
-
- /** Schedule delivery of this message to the given account. */
- protected void add(final RecipientType rt, final Account.Id to) {
- if (!rcptTo.contains(to) && isVisibleTo(to)) {
- rcptTo.add(to);
- add(rt, toAddress(to));
- }
- }
-
- private boolean isVisibleTo(final Account.Id to) {
- return projectState == null
- || change == null
- || projectState.controlFor(identifiedUserFactory.create(to))
- .controlFor(change).isVisible();
- }
-
- /** Schedule delivery of this message to the given account. */
- protected void add(final RecipientType rt, final Address addr) {
- if (addr != null && addr.email != null && addr.email.length() > 0) {
- smtpRcptTo.add(addr);
- switch (rt) {
- case TO:
- ((EmailHeader.AddressList) headers.get(HDR_TO)).add(addr);
- break;
- case CC:
- ((EmailHeader.AddressList) headers.get(HDR_CC)).add(addr);
- break;
- }
- }
- }
-
- private Address toAddress(final Account.Id id) {
- final Account a = accountCache.get(id).getAccount();
- final String e = a.getPreferredEmail();
- if (e == null) {
- return null;
- }
- return new Address(a.getFullName(), e);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java b/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
deleted file mode 100644
index 2430ff7aa6..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/ReplacePatchSetSender.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/** Send notice of new patch sets for reviewers. */
-public class ReplacePatchSetSender extends ReplyToChangeSender {
- public static interface Factory {
- public ReplacePatchSetSender create(Change change);
- }
-
- @Inject
- private SshInfo sshInfo;
-
- private final Set<Account.Id> reviewers = new HashSet<Account.Id>();
- private final Set<Account.Id> extraCC = new HashSet<Account.Id>();
-
- @Inject
- public ReplacePatchSetSender(@Assisted Change c) {
- super(c, "newpatchset");
- }
-
- public void addReviewers(final Collection<Account.Id> cc) {
- reviewers.addAll(cc);
- }
-
- public void addExtraCC(final Collection<Account.Id> cc) {
- extraCC.addAll(cc);
- }
-
- @Override
- protected void init() {
- super.init();
-
- if (fromId != null) {
- // Don't call yourself a reviewer of your own patch set.
- //
- reviewers.remove(fromId);
- }
- add(RecipientType.TO, reviewers);
- add(RecipientType.CC, extraCC);
- rcptToAuthors(RecipientType.CC);
- }
-
- @Override
- protected void format() {
- formatSalutation();
- formatChangeDetail();
-
- appendText("\n");
- appendText(" " + getPullUrl() + "\n");
- }
-
- private void formatSalutation() {
- final String changeUrl = getChangeUrl();
-
- if (reviewers.isEmpty()) {
- formatDest();
- if (changeUrl != null) {
- appendText("\n");
- appendText(" " + changeUrl + "\n");
- appendText("\n");
- }
- appendText("\n");
-
- } else {
- appendText("Hello");
- for (final Iterator<Account.Id> i = reviewers.iterator(); i.hasNext();) {
- appendText(" ");
- appendText(getNameFor(i.next()));
- appendText(",");
- }
- appendText("\n");
- appendText("\n");
-
- appendText("I'd like you to reexamine change "
- + change.getKey().abbreviate() + ".");
- if (changeUrl != null) {
- appendText(" Please visit\n");
- appendText("\n");
- appendText(" " + changeUrl + "\n");
- appendText("\n");
- appendText("to look at patch set " + patchSet.getPatchSetId());
- appendText(":\n");
- }
- appendText("\n");
-
- formatDest();
- appendText("\n");
- }
- }
-
- private void formatDest() {
- appendText("Change " + change.getKey().abbreviate());
- appendText(" (patch set " + patchSet.getPatchSetId() + ")");
- appendText(" for ");
- appendText(change.getDest().getShortName());
- appendText(" in ");
- appendText(projectName);
- appendText(":\n");
- }
-
- private String getPullUrl() {
- final StringBuilder r = new StringBuilder();
- r.append("git pull ssh://");
- String sshAddress = sshInfo.getSshdAddress();
- if (sshAddress.startsWith(":") || "".equals(sshAddress)) {
- r.append(getGerritHost());
- }
- r.append(sshAddress);
- r.append("/");
- r.append(projectName);
- r.append(" ");
- r.append(patchSet.getRefName());
- return r.toString();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java b/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
deleted file mode 100644
index 255a2ae2de..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/ReplyToChangeSender.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.client.reviewdb.Change;
-
-/** Alert a user to a reply to a change, usually commentary made during review. */
-public abstract class ReplyToChangeSender extends OutgoingEmail {
- protected ReplyToChangeSender(Change c, String mc) {
- super(c, mc);
- }
-
- @Override
- protected void init() {
- super.init();
-
- final String threadId = getChangeMessageThreadId();
- setHeader("In-Reply-To", threadId);
- setHeader("References", threadId);
-
- rcptToAuthors(RecipientType.TO);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java b/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
deleted file mode 100644
index d76d157b19..0000000000
--- a/src/main/java/com/google/gerrit/server/mail/SmtpEmailSender.java
+++ /dev/null
@@ -1,213 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import com.google.gerrit.pgm.Version;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.apache.commons.net.smtp.AuthSMTPClient;
-import org.apache.commons.net.smtp.SMTPClient;
-import org.apache.commons.net.smtp.SMTPReply;
-import org.eclipse.jgit.lib.Config;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/** Sends email via a nearby SMTP server. */
-@Singleton
-public class SmtpEmailSender implements EmailSender {
- public static enum Encryption {
- NONE, SSL, TLS;
- }
-
- private final boolean enabled;
-
- private String smtpHost;
- private int smtpPort;
- private String smtpUser;
- private String smtpPass;
- private Encryption smtpEncryption;
- private boolean sslVerify;
- private String[] allowrcpt;
-
- @Inject
- SmtpEmailSender(@GerritServerConfig final Config cfg) {
- enabled = cfg.getBoolean("sendemail", null, "enable", true);
-
- smtpHost = cfg.getString("sendemail", null, "smtpserver");
- if (smtpHost == null) {
- smtpHost = "127.0.0.1";
- }
-
- smtpEncryption =
- ConfigUtil.getEnum(cfg, "sendemail", null, "smtpencryption",
- Encryption.NONE);
- sslVerify = cfg.getBoolean("sendemail", null, "sslverify", true);
-
- final int defaultPort;
- switch (smtpEncryption) {
- case SSL:
- defaultPort = 465;
- break;
-
- case NONE:
- case TLS:
- default:
- defaultPort = 25;
- break;
- }
- smtpPort = cfg.getInt("sendemail", null, "smtpserverport", defaultPort);
-
- smtpUser = cfg.getString("sendemail", null, "smtpuser");
- smtpPass = cfg.getString("sendemail", null, "smtppass");
- allowrcpt = cfg.getStringList("sendemail", null, "allowrcpt");
- }
-
- @Override
- public boolean isEnabled() {
- return enabled;
- }
-
- @Override
- public void send(final Address from, final Collection<Address> rcpt,
- final Map<String, EmailHeader> callerHeaders, final String body)
- throws EmailException {
- if (!isEnabled()) {
- throw new EmailException("Sending email is disabled");
- }
-
- final Map<String, EmailHeader> hdrs =
- new LinkedHashMap<String, EmailHeader>(callerHeaders);
- setMissingHeader(hdrs, "MIME-Version", "1.0");
- setMissingHeader(hdrs, "Content-Type", "text/plain; charset=UTF-8");
- setMissingHeader(hdrs, "Content-Transfer-Encoding", "8bit");
- setMissingHeader(hdrs, "Content-Disposition", "inline");
- setMissingHeader(hdrs, "User-Agent", "Gerrit/" + Version.getVersion());
-
- try {
- final SMTPClient client = open();
- try {
- if (!client.setSender(from.email)) {
- throw new EmailException("Server " + smtpHost
- + " rejected from address " + from.email);
- }
-
- for (Address addr : rcpt) {
- if (!client.addRecipient(addr.email)) {
- String error = client.getReplyString();
- throw new EmailException("Server " + smtpHost
- + " rejected recipient " + addr + ": " + error);
- }
- }
-
- Writer w = client.sendMessageData();
- if (w == null) {
- throw new EmailException("Server " + smtpHost + " rejected body");
- }
- w = new BufferedWriter(w);
-
- for (Map.Entry<String, EmailHeader> h : hdrs.entrySet()) {
- if (!h.getValue().isEmpty()) {
- w.write(h.getKey());
- w.write(": ");
- h.getValue().write(w);
- w.write("\r\n");
- }
- }
-
- w.write("\r\n");
- w.write(body);
- w.flush();
- w.close();
-
- if (!client.completePendingCommand()) {
- throw new EmailException("Server " + smtpHost + " rejected body");
- }
-
- client.logout();
- } finally {
- client.disconnect();
- }
- } catch (IOException e) {
- throw new EmailException("Cannot send outgoing email", e);
- }
- }
-
- private void setMissingHeader(final Map<String, EmailHeader> hdrs,
- final String name, final String value) {
- if (!hdrs.containsKey(name) || hdrs.get(name).isEmpty()) {
- hdrs.put(name, new EmailHeader.String(value));
- }
- }
-
- private SMTPClient open() throws EmailException {
- final AuthSMTPClient client = new AuthSMTPClient("UTF-8");
- client.setAllowRcpt(allowrcpt);
-
- if (smtpEncryption == Encryption.SSL) {
- client.enableSSL(sslVerify);
- }
-
- try {
- client.connect(smtpHost, smtpPort);
- if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
- throw new EmailException("SMTP server rejected connection");
- }
- if (!client.login()) {
- String e = client.getReplyString();
- throw new EmailException("SMTP server rejected login: " + e);
- }
-
- if (smtpEncryption == Encryption.TLS) {
- if (!client.startTLS(smtpHost, smtpPort, sslVerify)) {
- throw new EmailException("SMTP server does not support TLS");
- }
- if (!client.login()) {
- String e = client.getReplyString();
- throw new EmailException("SMTP server rejected login: " + e);
- }
- }
-
- if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
- String e = client.getReplyString();
- throw new EmailException("SMTP server rejected auth: " + e);
- }
- } catch (IOException e) {
- if (client.isConnected()) {
- try {
- client.disconnect();
- } catch (IOException e2) {
- }
- }
- throw new EmailException(e.getMessage(), e);
- } catch (EmailException e) {
- if (client.isConnected()) {
- try {
- client.disconnect();
- } catch (IOException e2) {
- }
- }
- throw e;
- }
- return client;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/openid/OpenIdLoginServlet.java b/src/main/java/com/google/gerrit/server/openid/OpenIdLoginServlet.java
deleted file mode 100644
index f5ecb36ebc..0000000000
--- a/src/main/java/com/google/gerrit/server/openid/OpenIdLoginServlet.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.openid;
-
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import java.io.IOException;
-
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/** Handles the <code>/OpenID</code> URL for web based single-sign-on. */
-@SuppressWarnings("serial")
-@Singleton
-class OpenIdLoginServlet extends HttpServlet {
- private final OpenIdServiceImpl impl;
-
- @Inject
- OpenIdLoginServlet(final OpenIdServiceImpl i) {
- impl = i;
- }
-
- @Override
- public void doGet(final HttpServletRequest req, final HttpServletResponse rsp)
- throws IOException {
- doPost(req, rsp);
- }
-
- @Override
- public void doPost(final HttpServletRequest req, final HttpServletResponse rsp)
- throws IOException {
- try {
- rsp.setHeader("Expires", "Fri, 01 Jan 1980 00:00:00 GMT");
- rsp.setHeader("Pragma", "no-cache");
- rsp.setHeader("Cache-Control", "no-cache, must-revalidate");
- impl.doAuth(req, rsp);
- } catch (Exception e) {
- getServletContext().log("Unexpected error during authentication", e);
- rsp.reset();
- rsp.sendError(500);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/openid/OpenIdModule.java b/src/main/java/com/google/gerrit/server/openid/OpenIdModule.java
deleted file mode 100644
index 4205794585..0000000000
--- a/src/main/java/com/google/gerrit/server/openid/OpenIdModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.openid;
-
-import static java.util.concurrent.TimeUnit.MINUTES;
-
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-import com.google.inject.TypeLiteral;
-import com.google.inject.servlet.ServletModule;
-
-import java.util.List;
-
-/** Servlets and RPC support related to OpenID authentication. */
-public class OpenIdModule extends ServletModule {
- @Override
- protected void configureServlets() {
- install(new CacheModule() {
- @SuppressWarnings("unchecked")
- @Override
- protected void configure() {
- final TypeLiteral<Cache<String, List>> type =
- new TypeLiteral<Cache<String, List>>() {};
- core(type, "openid") //
- .maxAge(5, MINUTES) // don't cache too long, might be stale
- .memoryLimit(64) // short TTL means we won't have many entries
- ;
- }
- });
-
- serve("/" + OpenIdServiceImpl.RETURN_URL).with(OpenIdLoginServlet.class);
-
- install(new RpcServletModule(UiRpcModule.PREFIX) {
- @Override
- protected void configureServlets() {
- rpc(OpenIdServiceImpl.class);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/openid/OpenIdServiceImpl.java b/src/main/java/com/google/gerrit/server/openid/OpenIdServiceImpl.java
deleted file mode 100644
index aa956df4d4..0000000000
--- a/src/main/java/com/google/gerrit/server/openid/OpenIdServiceImpl.java
+++ /dev/null
@@ -1,468 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.openid;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.SignInDialog;
-import com.google.gerrit.client.SignInDialog.Mode;
-import com.google.gerrit.client.auth.openid.DiscoveryResult;
-import com.google.gerrit.client.auth.openid.OpenIdService;
-import com.google.gerrit.client.auth.openid.OpenIdUtil;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.UrlEncoded;
-import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.http.WebSession;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.KeyUtil;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-
-import org.openid4java.consumer.ConsumerException;
-import org.openid4java.consumer.ConsumerManager;
-import org.openid4java.consumer.VerificationResult;
-import org.openid4java.discovery.DiscoveryException;
-import org.openid4java.discovery.DiscoveryInformation;
-import org.openid4java.message.AuthRequest;
-import org.openid4java.message.Message;
-import org.openid4java.message.MessageException;
-import org.openid4java.message.MessageExtension;
-import org.openid4java.message.ParameterList;
-import org.openid4java.message.ax.AxMessage;
-import org.openid4java.message.ax.FetchRequest;
-import org.openid4java.message.ax.FetchResponse;
-import org.openid4java.message.sreg.SRegMessage;
-import org.openid4java.message.sreg.SRegRequest;
-import org.openid4java.message.sreg.SRegResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.List;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-@Singleton
-class OpenIdServiceImpl implements OpenIdService {
- private static final Logger log =
- LoggerFactory.getLogger(OpenIdServiceImpl.class);
-
- static final String RETURN_URL = "OpenID";
-
- private static final String P_MODE = "gerrit.mode";
- private static final String P_TOKEN = "gerrit.token";
- private static final String P_REMEMBER = "gerrit.remember";
- private static final String P_CLAIMED = "gerrit.claimed";
- private static final int LASTID_AGE = 365 * 24 * 60 * 60; // seconds
-
- private static final String OPENID_MODE = "openid.mode";
- private static final String OMODE_CANCEL = "cancel";
-
- private static final String SCHEMA_EMAIL =
- "http://schema.openid.net/contact/email";
- private static final String SCHEMA_FIRSTNAME =
- "http://schema.openid.net/namePerson/first";
- private static final String SCHEMA_LASTNAME =
- "http://schema.openid.net/namePerson/last";
-
- private final Provider<WebSession> webSession;
- private final Provider<IdentifiedUser> identifiedUser;
- private final Provider<String> urlProvider;
- private final AccountManager accountManager;
- private final ConsumerManager manager;
- private final SelfPopulatingCache<String, List> discoveryCache;
-
- @Inject
- OpenIdServiceImpl(final Provider<WebSession> cf,
- final Provider<IdentifiedUser> iu,
- @CanonicalWebUrl @Nullable final Provider<String> up,
- @Named("openid") final Cache<String, List> openidCache,
- final AccountManager am) throws ConsumerException {
- webSession = cf;
- identifiedUser = iu;
- urlProvider = up;
- accountManager = am;
- manager = new ConsumerManager();
-
- discoveryCache = new SelfPopulatingCache<String, List>(openidCache) {
- @Override
- protected List createEntry(final String url) throws Exception {
- try {
- final List<?> list = manager.discover(url);
- return list != null && !list.isEmpty() ? list : null;
- } catch (DiscoveryException e) {
- return null;
- }
- }
- };
- }
-
- public void discover(final String openidIdentifier,
- final SignInDialog.Mode mode, final boolean remember,
- final String returnToken, final AsyncCallback<DiscoveryResult> callback) {
- final State state;
- state = init(openidIdentifier, mode, remember, returnToken);
- if (state == null) {
- callback.onSuccess(new DiscoveryResult(false));
- return;
- }
-
- final AuthRequest aReq;
- try {
- aReq = manager.authenticate(state.discovered, state.retTo.toString());
- aReq.setRealm(state.contextUrl);
-
- if (requestRegistration(aReq)) {
- final SRegRequest sregReq = SRegRequest.createFetchRequest();
- sregReq.addAttribute("fullname", true);
- sregReq.addAttribute("email", true);
- aReq.addExtension(sregReq);
-
- final FetchRequest fetch = FetchRequest.createFetchRequest();
- fetch.addAttribute("FirstName", SCHEMA_FIRSTNAME, true);
- fetch.addAttribute("LastName", SCHEMA_LASTNAME, true);
- fetch.addAttribute("Email", SCHEMA_EMAIL, true);
- aReq.addExtension(fetch);
- }
- } catch (MessageException e) {
- callback.onSuccess(new DiscoveryResult(false));
- return;
- } catch (ConsumerException e) {
- callback.onSuccess(new DiscoveryResult(false));
- return;
- }
-
- callback.onSuccess(new DiscoveryResult(true, aReq.getDestinationUrl(false),
- aReq.getParameterMap()));
- }
-
- private boolean requestRegistration(final AuthRequest aReq) {
- if (AuthRequest.SELECT_ID.equals(aReq.getIdentity())) {
- // We don't know anything about the identity, as the provider
- // will offer the user a way to indicate their identity. Skip
- // any database query operation and assume we must ask for the
- // registration information, in case the identity is new to us.
- //
- return true;
-
- }
-
- // We might already have this account on file. Look for it.
- //
- try {
- return accountManager.lookup(aReq.getIdentity()) == null;
- } catch (AccountException e) {
- log.warn("Cannot determine if user account exists", e);
- return true;
- }
- }
-
- /** Called by {@link OpenIdLoginServlet} doGet, doPost */
- void doAuth(final HttpServletRequest req, final HttpServletResponse rsp)
- throws Exception {
- if (OMODE_CANCEL.equals(req.getParameter(OPENID_MODE))) {
- cancel(req, rsp);
- return;
- }
-
- // Process the authentication response.
- //
- final SignInDialog.Mode mode = signInMode(req);
- final String openidIdentifier = req.getParameter("openid.identity");
- final String claimedIdentifier = req.getParameter(P_CLAIMED);
- final String returnToken = req.getParameter(P_TOKEN);
- final boolean remember = "1".equals(req.getParameter(P_REMEMBER));
- final String rediscoverIdentifier =
- claimedIdentifier != null ? claimedIdentifier : openidIdentifier;
- final State state;
-
- state = init(rediscoverIdentifier, mode, remember, returnToken);
- if (state == null) {
- // Re-discovery must have failed, we can't run a login.
- //
- cancel(req, rsp);
- return;
- }
-
- final String returnTo = req.getParameter("openid.return_to");
- if (returnTo != null && returnTo.contains("openid.rpnonce=")) {
- // Some providers (claimid.com) seem to embed these request
- // parameters into our return_to URL, and then give us them
- // in the return_to request parameter. But not all.
- //
- state.retTo.put("openid.rpnonce", req.getParameter("openid.rpnonce"));
- state.retTo.put("openid.rpsig", req.getParameter("openid.rpsig"));
- }
-
- final VerificationResult result =
- manager.verify(state.retTo.toString(), new ParameterList(req
- .getParameterMap()), state.discovered);
- if (result.getVerifiedId() == null /* authentication failure */) {
- if ("Nonce verification failed.".equals(result.getStatusMsg())) {
- // We might be suffering from clock skew on this system.
- //
- log.error("OpenID failure: " + result.getStatusMsg()
- + " Likely caused by clock skew on this server,"
- + " install/configure NTP.");
- cancelWithError(req, rsp, result.getStatusMsg());
-
- } else if (result.getStatusMsg() != null) {
- // Authentication failed.
- //
- log.error("OpenID failure: " + result.getStatusMsg());
- cancelWithError(req, rsp, result.getStatusMsg());
-
- } else {
- // Assume authentication was canceled.
- //
- cancel(req, rsp);
- }
- return;
- }
-
- final Message authRsp = result.getAuthResponse();
- SRegResponse sregRsp = null;
- FetchResponse fetchRsp = null;
-
- if (authRsp.hasExtension(SRegMessage.OPENID_NS_SREG)) {
- final MessageExtension ext =
- authRsp.getExtension(SRegMessage.OPENID_NS_SREG);
- if (ext instanceof SRegResponse) {
- sregRsp = (SRegResponse) ext;
- }
- }
-
- if (authRsp.hasExtension(AxMessage.OPENID_NS_AX)) {
- final MessageExtension ext = authRsp.getExtension(AxMessage.OPENID_NS_AX);
- if (ext instanceof FetchResponse) {
- fetchRsp = (FetchResponse) ext;
- }
- }
-
- final com.google.gerrit.server.account.AuthRequest areq =
- new com.google.gerrit.server.account.AuthRequest(openidIdentifier);
-
- if (sregRsp != null) {
- areq.setDisplayName(sregRsp.getAttributeValue("fullname"));
- areq.setEmailAddress(sregRsp.getAttributeValue("email"));
-
- } else if (fetchRsp != null) {
- final String firstName = fetchRsp.getAttributeValue("FirstName");
- final String lastName = fetchRsp.getAttributeValue("LastName");
- final StringBuilder n = new StringBuilder();
- if (firstName != null && firstName.length() > 0) {
- n.append(firstName);
- }
- if (lastName != null && lastName.length() > 0) {
- if (n.length() > 0) {
- n.append(' ');
- }
- n.append(lastName);
- }
- areq.setDisplayName(n.length() > 0 ? n.toString() : null);
- areq.setEmailAddress(fetchRsp.getAttributeValue("Email"));
- }
-
- if (claimedIdentifier != null) {
- // The user used a claimed identity which has delegated to the verified
- // identity we have in our AuthRequest above. We still should have a
- // link between the two, so set one up if not present.
- //
- Account.Id claimedId = accountManager.lookup(claimedIdentifier);
- Account.Id actualId = accountManager.lookup(areq.getExternalId());
-
- if (claimedId != null && actualId != null) {
- if (claimedId.equals(actualId)) {
- // Both link to the same account, that's what we expected.
- } else {
- // This is (for now) a fatal error. There are two records
- // for what might be the same user.
- //
- log.error("OpenID accounts disagree over user identity:\n"
- + " Claimed ID: " + claimedId + " is " + claimedIdentifier
- + "\n" + " Delgate ID: " + actualId + " is "
- + areq.getExternalId());
- cancelWithError(req, rsp, "Contact site administrator");
- return;
- }
-
- } else if (claimedId == null && actualId != null) {
- // Older account, the actual was already created but the claimed
- // was missing due to a bug in Gerrit. Link the claimed.
- //
- final com.google.gerrit.server.account.AuthRequest linkReq =
- new com.google.gerrit.server.account.AuthRequest(claimedIdentifier);
- linkReq.setDisplayName(areq.getDisplayName());
- linkReq.setEmailAddress(areq.getEmailAddress());
- accountManager.link(actualId, linkReq);
-
- } else if (claimedId != null && actualId == null) {
- // Claimed account already exists, but it smells like the user has
- // changed their delegate to point to a different provider. Link
- // the new provider.
- //
- accountManager.link(claimedId, areq);
-
- } else {
- // Both are null, we are going to create a new account below.
- }
- }
-
- try {
- switch (mode) {
- case REGISTER:
- case SIGN_IN:
- final com.google.gerrit.server.account.AuthResult arsp;
- arsp = accountManager.authenticate(areq);
-
- final Cookie lastId = new Cookie(OpenIdUtil.LASTID_COOKIE, "");
- lastId.setPath(req.getContextPath() + "/");
- if (remember) {
- lastId.setValue(rediscoverIdentifier);
- lastId.setMaxAge(LASTID_AGE);
- } else {
- lastId.setMaxAge(0);
- }
- rsp.addCookie(lastId);
- webSession.get().login(arsp.getAccountId(), remember);
- if (arsp.isNew() && claimedIdentifier != null) {
- final com.google.gerrit.server.account.AuthRequest linkReq =
- new com.google.gerrit.server.account.AuthRequest(
- claimedIdentifier);
- linkReq.setDisplayName(areq.getDisplayName());
- linkReq.setEmailAddress(areq.getEmailAddress());
- accountManager.link(arsp.getAccountId(), linkReq);
- }
- callback(arsp.isNew(), req, rsp);
- break;
-
- case LINK_IDENTIY:
- accountManager.link(identifiedUser.get().getAccountId(), areq);
- callback(false, req, rsp);
- break;
- }
- } catch (AccountException e) {
- log.error("OpenID authentication failure", e);
- cancelWithError(req, rsp, "Contact site administrator");
- }
- }
-
- private boolean isSignIn(final SignInDialog.Mode mode) {
- switch (mode) {
- case SIGN_IN:
- case REGISTER:
- return true;
- default:
- return false;
- }
- }
-
- private static Mode signInMode(final HttpServletRequest req) {
- try {
- return SignInDialog.Mode.valueOf(req.getParameter(P_MODE));
- } catch (RuntimeException e) {
- return SignInDialog.Mode.SIGN_IN;
- }
- }
-
- private void callback(final boolean isNew, final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- String token = req.getParameter(P_TOKEN);
- if (token == null || token.isEmpty() || token.startsWith("SignInFailure,")) {
- token = Link.MINE;
- }
-
- final StringBuilder rdr = new StringBuilder();
- rdr.append(urlProvider.get());
- rdr.append('#');
- if (isNew && !token.startsWith(Link.REGISTER + ",")) {
- rdr.append(Link.REGISTER);
- rdr.append(',');
- }
- rdr.append(token);
- rsp.sendRedirect(rdr.toString());
- }
-
- private void cancel(final HttpServletRequest req,
- final HttpServletResponse rsp) throws IOException {
- if (isSignIn(signInMode(req))) {
- webSession.get().logout();
- }
- callback(false, req, rsp);
- }
-
- private void cancelWithError(final HttpServletRequest req,
- final HttpServletResponse rsp, final String errorDetail)
- throws IOException {
- final SignInDialog.Mode mode = signInMode(req);
- if (isSignIn(mode)) {
- webSession.get().logout();
- }
- final StringBuilder rdr = new StringBuilder();
- rdr.append(urlProvider.get());
- rdr.append('#');
- rdr.append("SignInFailure");
- rdr.append(',');
- rdr.append(mode.name());
- rdr.append(',');
- rdr.append(errorDetail != null ? KeyUtil.encode(errorDetail) : "");
- rsp.sendRedirect(rdr.toString());
- }
-
- private State init(final String openidIdentifier,
- final SignInDialog.Mode mode, final boolean remember,
- final String returnToken) {
- final List<?> list = discoveryCache.get(openidIdentifier);
- if (list == null || list.isEmpty()) {
- return null;
- }
-
- final String contextUrl = urlProvider.get();
- final DiscoveryInformation discovered = manager.associate(list);
- final UrlEncoded retTo = new UrlEncoded(contextUrl + RETURN_URL);
- retTo.put(P_MODE, mode.name());
- if (returnToken != null && returnToken.length() > 0) {
- retTo.put(P_TOKEN, returnToken);
- }
- if (remember) {
- retTo.put(P_REMEMBER, "1");
- }
- if (discovered.hasClaimedIdentifier()) {
- retTo.put(P_CLAIMED, discovered.getClaimedIdentifier().getIdentifier());
- }
- return new State(discovered, retTo, contextUrl);
- }
-
- private static class State {
- final DiscoveryInformation discovered;
- final UrlEncoded retTo;
- final String contextUrl;
-
- State(final DiscoveryInformation d, final UrlEncoded r, final String c) {
- discovered = d;
- retTo = r;
- contextUrl = c;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchFile.java b/src/main/java/com/google/gerrit/server/patch/PatchFile.java
deleted file mode 100644
index 6bda2cc0ad..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchFile.java
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.rpc.CorruptEntityException;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.TreeWalk;
-
-import java.io.IOException;
-import java.nio.charset.CharacterCodingException;
-
-/** State supporting processing of a single {@link Patch} instance. */
-public class PatchFile {
- private final Repository repo;
- private final PatchListEntry entry;
- private final RevTree aTree;
- private final RevTree bTree;
-
- private Text a;
- private Text b;
-
- public PatchFile(final Repository repo, final PatchList patchList,
- final String fileName) throws MissingObjectException,
- IncorrectObjectTypeException, IOException {
- this.repo = repo;
- this.entry = patchList.get(fileName);
-
- final RevWalk rw = new RevWalk(repo);
- final RevCommit bCommit = rw.parseCommit(patchList.getNewId());
- if (patchList.getOldId() != null) {
- aTree = rw.parseTree(patchList.getOldId());
- } else {
- final RevCommit p = bCommit.getParent(0);
- rw.parseHeaders(p);
- aTree = p.getTree();
- }
- bTree = bCommit.getTree();
- }
-
- /**
- * Extract a line from the file, as a string.
- *
- * @param file the file index to extract.
- * @param line the line number to extract (1 based; 1 is the first line).
- * @return the string version of the file line.
- * @throws CorruptEntityException the patch cannot be read.
- * @throws IOException the patch or complete file content cannot be read.
- * @throws NoSuchEntityException
- * @throws CharacterCodingException the file is not a known character set.
- */
- public String getLine(final int file, final int line)
- throws CorruptEntityException, IOException, NoSuchEntityException {
- switch (file) {
- case 0:
- if (a == null) {
- a = load(aTree, entry.getOldName());
- }
- return a.getLine(line - 1);
-
- case 1:
- if (b == null) {
- b = load(bTree, entry.getNewName());
- }
- return b.getLine(line - 1);
-
- default:
- throw new NoSuchEntityException();
- }
- }
-
- private Text load(final ObjectId tree, final String path)
- throws MissingObjectException, IncorrectObjectTypeException,
- CorruptObjectException, IOException {
- if (path == null) {
- return Text.EMPTY;
- }
- final TreeWalk tw = TreeWalk.forPath(repo, path, tree);
- if (tw == null) {
- return Text.EMPTY;
- }
- if (tw.getFileMode(0).getObjectType() != Constants.OBJ_BLOB) {
- return Text.EMPTY;
- }
- final ObjectId id = tw.getObjectId(0);
- final ObjectLoader ldr = repo.openObject(id);
- if (ldr == null) {
- throw new MissingObjectException(id, Constants.TYPE_BLOB);
- }
- return new Text(ldr.getCachedBytes());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchList.java b/src/main/java/com/google/gerrit/server/patch/PatchList.java
deleted file mode 100644
index a1a3137214..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchList.java
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-
-import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.readCanBeNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.writeCanBeNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
-
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.server.config.Nullable;
-
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.InflaterInputStream;
-
-public class PatchList implements Serializable {
- private static final long serialVersionUID = PatchListKey.serialVersionUID;
- private static final Comparator<PatchListEntry> PATCH_CMP =
- new Comparator<PatchListEntry>() {
- @Override
- public int compare(final PatchListEntry a, final PatchListEntry b) {
- return a.getNewName().compareTo(b.getNewName());
- }
- };
-
- @Nullable
- private transient ObjectId oldId;
- private transient ObjectId newId;
- private transient PatchListEntry[] patches;
-
- PatchList(@Nullable final AnyObjectId oldId, final AnyObjectId newId,
- final PatchListEntry[] patches) {
- this.oldId = oldId != null ? oldId.copy() : null;
- this.newId = newId.copy();
-
- Arrays.sort(patches, PATCH_CMP);
- this.patches = patches;
- }
-
- /** Old side tree or commit; null only if this is a combined diff. */
- @Nullable
- public ObjectId getOldId() {
- return oldId;
- }
-
- /** New side commit. */
- public ObjectId getNewId() {
- return newId;
- }
-
- /** Get a sorted, unmodifiable list of all files in this list. */
- public List<PatchListEntry> getPatches() {
- return Collections.unmodifiableList(Arrays.asList(patches));
- }
-
- /**
- * Get a sorted, modifiable list of all files in this list.
- * <p>
- * The returned list items do not populate:
- * <ul>
- * <li>{@link Patch#getCommentCount()}
- * <li>{@link Patch#getDraftCount()}
- * <li>{@link Patch#isReviewedByCurrentUser()}
- * </ul>
- *
- * @param setId the patch set identity these patches belong to. This really
- * should not need to be specified, but is a current legacy artifact of
- * how the cache is keyed versus how the database is keyed.
- */
- public List<Patch> toPatchList(final PatchSet.Id setId) {
- final ArrayList<Patch> r = new ArrayList<Patch>(patches.length);
- for (final PatchListEntry e : patches) {
- r.add(e.toPatch(setId));
- }
- return r;
- }
-
- /** Find an entry by name, returning an empty entry if not present. */
- public PatchListEntry get(final String fileName) {
- final int index = search(fileName);
- return 0 <= index ? patches[index] : PatchListEntry.empty(fileName);
- }
-
- private int search(final String fileName) {
- int high = patches.length;
- int low = 0;
- while (low < high) {
- final int mid = (low + high) >>> 1;
- final int cmp = patches[mid].getNewName().compareTo(fileName);
- if (cmp < 0)
- low = mid + 1;
- else if (cmp == 0)
- return mid;
- else
- high = mid;
- }
- return -(low + 1);
- }
-
- private void writeObject(final ObjectOutputStream output) throws IOException {
- final ByteArrayOutputStream buf = new ByteArrayOutputStream();
- final DeflaterOutputStream out = new DeflaterOutputStream(buf);
- try {
- writeCanBeNull(out, oldId);
- writeNotNull(out, newId);
- writeVarInt32(out, patches.length);
- for (PatchListEntry p : patches) {
- p.writeTo(out);
- }
- } finally {
- out.close();
- }
- writeBytes(output, buf.toByteArray());
- }
-
- private void readObject(final ObjectInputStream input) throws IOException {
- final ByteArrayInputStream buf = new ByteArrayInputStream(readBytes(input));
- final InflaterInputStream in = new InflaterInputStream(buf);
- try {
- oldId = readCanBeNull(in);
- newId = readNotNull(in);
- final int cnt = readVarInt32(in);
- final PatchListEntry[] all = new PatchListEntry[cnt];
- for (int i = 0; i < all.length; i++) {
- all[i] = PatchListEntry.readFrom(in);
- }
- patches = all;
- } finally {
- in.close();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchListCache.java b/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
deleted file mode 100644
index a73ab4ccbc..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchListCache.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-
-/** Provides a cached list of {@link PatchListEntry}. */
-public interface PatchListCache {
- public PatchList get(PatchListKey key);
-
- public PatchList get(Change change, PatchSet patchSet);
-
- public PatchList get(Change change, PatchSet patchSet, Whitespace whitespace);
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java b/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
deleted file mode 100644
index bf55c15688..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchListCacheImpl.java
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import static com.google.gerrit.client.data.PatchScriptSettings.Whitespace.IGNORE_NONE;
-
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.EvictionPolicy;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/** Provides a cached list of {@link PatchListEntry}. */
-@Singleton
-public class PatchListCacheImpl implements PatchListCache {
- private static final String CACHE_NAME = "diff";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<PatchListKey, PatchList>> type =
- new TypeLiteral<Cache<PatchListKey, PatchList>>() {};
- disk(type, CACHE_NAME) //
- .memoryLimit(128) // very large items, cache only a few
- .evictionPolicy(EvictionPolicy.LRU) // prefer most recent
- ;
- bind(PatchListCacheImpl.class);
- bind(PatchListCache.class).to(PatchListCacheImpl.class);
- }
- };
- }
-
- private final GitRepositoryManager repoManager;
- private final SelfPopulatingCache<PatchListKey, PatchList> self;
-
- @Inject
- PatchListCacheImpl(final GitRepositoryManager grm,
- @Named(CACHE_NAME) final Cache<PatchListKey, PatchList> raw) {
- repoManager = grm;
- self = new SelfPopulatingCache<PatchListKey, PatchList>(raw) {
- @Override
- protected PatchList createEntry(final PatchListKey key) throws Exception {
- return compute(key);
- }
- };
- }
-
- public PatchList get(final PatchListKey key) {
- return self.get(key);
- }
-
- public PatchList get(final Change change, final PatchSet patchSet) {
- return get(change, patchSet, IGNORE_NONE);
- }
-
- public PatchList get(final Change change, final PatchSet patchSet,
- final Whitespace whitespace) {
- final Project.NameKey projectKey = change.getProject();
- final ObjectId a = null;
- final ObjectId b = ObjectId.fromString(patchSet.getRevision().get());
- return get(new PatchListKey(projectKey, a, b, whitespace));
- }
-
- private PatchList compute(final PatchListKey key)
- throws MissingObjectException, IncorrectObjectTypeException, IOException {
- final Repository repo = repoManager.openRepository(key.projectKey.get());
- try {
- return readPatchList(key, repo);
- } finally {
- repo.close();
- }
- }
-
- private PatchList readPatchList(final PatchListKey key, final Repository repo)
- throws IOException {
- final RevCommit b = new RevWalk(repo).parseCommit(key.getNewId());
- final AnyObjectId a = aFor(key, repo, b);
-
- final List<String> args = new ArrayList<String>();
- args.add("git");
- args.add("--git-dir=.");
- args.add("diff-tree");
- args.add("-M");
- switch (key.getWhitespace()) {
- case IGNORE_NONE:
- break;
- case IGNORE_SPACE_AT_EOL:
- args.add("--ignore-space-at-eol");
- break;
- case IGNORE_SPACE_CHANGE:
- args.add("--ignore-space-change");
- break;
- case IGNORE_ALL_SPACE:
- args.add("--ignore-all-space");
- break;
- default:
- throw new IOException("Unsupported whitespace " + key.getWhitespace());
- }
- if (a == null /* want combined diff */) {
- args.add("--cc");
- args.add(b.name());
- } else {
- args.add("--unified=1");
- args.add(a.name());
- args.add(b.name());
- }
-
- final org.eclipse.jgit.patch.Patch p = new org.eclipse.jgit.patch.Patch();
- final Process diffProcess = exec(repo, args);
- try {
- diffProcess.getOutputStream().close();
- diffProcess.getErrorStream().close();
-
- final InputStream in = diffProcess.getInputStream();
- try {
- p.parse(in);
- } finally {
- in.close();
- }
- } finally {
- try {
- final int rc = diffProcess.waitFor();
- if (rc != 0) {
- throw new IOException("git diff-tree exited abnormally: " + rc);
- }
- } catch (InterruptedException ie) {
- }
- }
-
- final int cnt = p.getFiles().size();
- final PatchListEntry[] entries = new PatchListEntry[cnt];
- for (int i = 0; i < cnt; i++) {
- entries[i] = new PatchListEntry(p.getFiles().get(i));
- }
- return new PatchList(a, b, entries);
- }
-
- private static AnyObjectId aFor(final PatchListKey key,
- final Repository repo, final RevCommit b) throws IOException {
- if (key.getOldId() != null) {
- return key.getOldId();
- }
-
- switch (b.getParentCount()) {
- case 0:
- return emptyTree(repo);
- case 1:
- return b.getParent(0);
- default:
- // merge commit, return null to force combined diff behavior
- return null;
- }
- }
-
- private static Process exec(final Repository repo, final List<String> args)
- throws IOException {
- final String[] argv = args.toArray(new String[args.size()]);
- return Runtime.getRuntime().exec(argv, null, repo.getDirectory());
- }
-
- private static ObjectId emptyTree(final Repository repo) throws IOException {
- return new ObjectWriter(repo).writeCanonicalTree(new byte[0]);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java b/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
deleted file mode 100644
index a17fc98f11..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchListEntry.java
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
-
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Patch.ChangeType;
-import com.google.gerrit.client.reviewdb.Patch.PatchType;
-
-import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.patch.CombinedFileHeader;
-import org.eclipse.jgit.patch.FileHeader;
-import org.eclipse.jgit.util.IntList;
-import org.eclipse.jgit.util.RawParseUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-public class PatchListEntry {
- private static final byte[] EMPTY_HEADER = {};
-
- static PatchListEntry empty(final String fileName) {
- return new PatchListEntry(ChangeType.MODIFIED, PatchType.UNIFIED, null,
- fileName, EMPTY_HEADER, Collections.<Edit> emptyList());
- }
-
- private final ChangeType changeType;
- private final PatchType patchType;
- private final String oldName;
- private final String newName;
- private final byte[] header;
- private final List<Edit> edits;
-
- PatchListEntry(final FileHeader hdr) {
- changeType = toChangeType(hdr);
- patchType = toPatchType(hdr);
-
- switch (changeType) {
- case DELETED:
- oldName = null;
- newName = hdr.getOldName();
- break;
-
- case ADDED:
- case MODIFIED:
- oldName = null;
- newName = hdr.getNewName();
- break;
-
- case COPIED:
- case RENAMED:
- oldName = hdr.getOldName();
- newName = hdr.getNewName();
- break;
-
- default:
- throw new IllegalArgumentException("Unsupported type " + changeType);
- }
-
- header = compact(hdr);
-
- if (hdr instanceof CombinedFileHeader
- || hdr.getHunks().isEmpty() //
- || hdr.getOldMode() == FileMode.GITLINK
- || hdr.getNewMode() == FileMode.GITLINK) {
- edits = Collections.emptyList();
- } else {
- edits = Collections.unmodifiableList(hdr.toEditList());
- }
- }
-
- private PatchListEntry(final ChangeType changeType,
- final PatchType patchType, final String oldName, final String newName,
- final byte[] header, final List<Edit> edits) {
- this.changeType = changeType;
- this.patchType = patchType;
- this.oldName = oldName;
- this.newName = newName;
- this.header = header;
- this.edits = edits;
- }
-
- public ChangeType getChangeType() {
- return changeType;
- }
-
- public PatchType getPatchType() {
- return patchType;
- }
-
- public String getOldName() {
- return oldName;
- }
-
- public String getNewName() {
- return newName;
- }
-
- public List<Edit> getEdits() {
- return edits;
- }
-
- public List<String> getHeaderLines() {
- final IntList m = RawParseUtils.lineMap(header, 0, header.length);
- final List<String> headerLines = new ArrayList<String>(m.size() - 1);
- for (int i = 1; i < m.size() - 1; i++) {
- final int b = m.get(i);
- final int e = m.get(i + 1);
- headerLines.add(RawParseUtils.decode(Constants.CHARSET, header, b, e));
- }
- return headerLines;
- }
-
- Patch toPatch(final PatchSet.Id setId) {
- final Patch p = new Patch(new Patch.Key(setId, getNewName()));
- p.setChangeType(getChangeType());
- p.setPatchType(getPatchType());
- p.setSourceFileName(getOldName());
- return p;
- }
-
- void writeTo(final OutputStream out) throws IOException {
- writeEnum(out, changeType);
- writeEnum(out, patchType);
- writeString(out, oldName);
- writeString(out, newName);
- writeBytes(out, header);
-
- writeVarInt32(out, edits.size());
- for (final Edit e : edits) {
- writeVarInt32(out, e.getBeginA());
- writeVarInt32(out, e.getEndA());
- writeVarInt32(out, e.getBeginB());
- writeVarInt32(out, e.getEndB());
- }
- }
-
- static PatchListEntry readFrom(final InputStream in) throws IOException {
- final ChangeType changeType = readEnum(in, ChangeType.values());
- final PatchType patchType = readEnum(in, PatchType.values());
- final String oldName = readString(in);
- final String newName = readString(in);
- final byte[] hdr = readBytes(in);
-
- final int editCount = readVarInt32(in);
- final Edit[] editArray = new Edit[editCount];
- for (int i = 0; i < editCount; i++) {
- final int beginA = readVarInt32(in);
- final int endA = readVarInt32(in);
- final int beginB = readVarInt32(in);
- final int endB = readVarInt32(in);
- editArray[i] = new Edit(beginA, endA, beginB, endB);
- }
-
- return new PatchListEntry(changeType, patchType, oldName, newName, hdr,
- Collections.unmodifiableList(Arrays.asList(editArray)));
- }
-
- private static byte[] compact(final FileHeader h) {
- final int end = end(h);
- if (h.getStartOffset() == 0 && end == h.getBuffer().length) {
- return h.getBuffer();
- }
-
- final byte[] buf = new byte[end - h.getStartOffset()];
- System.arraycopy(h.getBuffer(), h.getStartOffset(), buf, 0, buf.length);
- return buf;
- }
-
- private static int end(final FileHeader h) {
- if (h instanceof CombinedFileHeader) {
- return h.getEndOffset();
- }
- if (!h.getHunks().isEmpty()) {
- return h.getHunks().get(0).getStartOffset();
- }
- return h.getEndOffset();
- }
-
- private static ChangeType toChangeType(final FileHeader hdr) {
- switch (hdr.getChangeType()) {
- case ADD:
- return Patch.ChangeType.ADDED;
- case MODIFY:
- return Patch.ChangeType.MODIFIED;
- case DELETE:
- return Patch.ChangeType.DELETED;
- case RENAME:
- return Patch.ChangeType.RENAMED;
- case COPY:
- return Patch.ChangeType.COPIED;
- default:
- throw new IllegalArgumentException("Unsupported type "
- + hdr.getChangeType());
- }
- }
-
- private static PatchType toPatchType(final FileHeader hdr) {
- PatchType pt;
-
- if (hdr instanceof CombinedFileHeader) {
- pt = Patch.PatchType.N_WAY;
- } else {
- switch (hdr.getPatchType()) {
- case UNIFIED:
- pt = Patch.PatchType.UNIFIED;
- break;
- case GIT_BINARY:
- case BINARY:
- pt = Patch.PatchType.BINARY;
- break;
- default:
- throw new IllegalArgumentException("Unsupported type "
- + hdr.getPatchType());
- }
- }
-
- if (pt != PatchType.BINARY) {
- final byte[] buf = hdr.getBuffer();
- for (int ptr = hdr.getStartOffset(); ptr < hdr.getEndOffset(); ptr++) {
- if (buf[ptr] == '\0') {
- // Its really binary, but Git couldn't see the nul early enough
- // to realize its binary, and instead produced the diff.
- //
- // Force it to be a binary; it really should have been that.
- //
- pt = PatchType.BINARY;
- break;
- }
- }
- }
-
- return pt;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchListKey.java b/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
deleted file mode 100644
index a04139a3fb..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchListKey.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.readCanBeNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.readNotNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.writeCanBeNull;
-import static org.eclipse.jgit.lib.ObjectIdSerialization.writeNotNull;
-
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.server.config.Nullable;
-
-import org.eclipse.jgit.lib.AnyObjectId;
-import org.eclipse.jgit.lib.ObjectId;
-
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-
-public class PatchListKey implements Serializable {
- static final long serialVersionUID = 9L;
-
- private transient ObjectId oldId;
- private transient ObjectId newId;
- private transient Whitespace whitespace;
-
- transient Project.NameKey projectKey; // not required to form the key
-
- public PatchListKey(final Project.NameKey pk, final AnyObjectId a,
- final AnyObjectId b, final Whitespace ws) {
- projectKey = pk;
- oldId = a != null ? a.copy() : null;
- newId = b.copy();
- whitespace = ws;
- }
-
- /** Old side commit, or null to assume ancestor or combined merge. */
- @Nullable
- public ObjectId getOldId() {
- return oldId;
- }
-
- /** New side commit name. */
- public ObjectId getNewId() {
- return newId;
- }
-
- public Whitespace getWhitespace() {
- return whitespace;
- }
-
- @Override
- public int hashCode() {
- int h = 0;
-
- if (oldId != null) {
- h = h * 31 + oldId.hashCode();
- }
-
- h = h * 31 + newId.hashCode();
- h = h * 31 + whitespace.name().hashCode();
-
- return h;
- }
-
- @Override
- public boolean equals(final Object o) {
- if (o instanceof PatchListKey) {
- final PatchListKey k = (PatchListKey) o;
- return eq(oldId, k.oldId) //
- && eq(newId, k.newId) //
- && whitespace == k.whitespace;
- }
- return false;
- }
-
- private static boolean eq(final ObjectId a, final ObjectId b) {
- if (a == null && b == null) {
- return true;
- }
- return a != null && b != null && AnyObjectId.equals(a, b);
- }
-
- private void writeObject(final ObjectOutputStream out) throws IOException {
- writeCanBeNull(out, oldId);
- writeNotNull(out, newId);
- writeEnum(out, whitespace);
- }
-
- private void readObject(final ObjectInputStream in) throws IOException {
- oldId = readCanBeNull(in);
- newId = readNotNull(in);
- whitespace = readEnum(in, Whitespace.values());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
deleted file mode 100644
index 81c1051900..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.UserIdentity;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.account.AccountByEmailCache;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.Set;
-
-
-/**
- * Factory class creating PatchSetInfo from meta-data found in Git repository.
- */
-@Singleton
-public class PatchSetInfoFactory {
- private final GitRepositoryManager repoManager;
- private final SchemaFactory<ReviewDb> schemaFactory;
- private final AccountByEmailCache byEmailCache;
-
- @Inject
- public PatchSetInfoFactory(final GitRepositoryManager grm,
- final SchemaFactory<ReviewDb> schemaFactory,
- final AccountByEmailCache byEmailCache) {
- this.repoManager = grm;
- this.schemaFactory = schemaFactory;
- this.byEmailCache = byEmailCache;
- }
-
- public PatchSetInfo get(RevCommit src, PatchSet.Id psi) {
- PatchSetInfo info = new PatchSetInfo(psi);
- info.setSubject(src.getShortMessage());
- info.setMessage(src.getFullMessage());
- info.setAuthor(toUserIdentity(src.getAuthorIdent()));
- info.setCommitter(toUserIdentity(src.getCommitterIdent()));
-
- return info;
- }
-
- public PatchSetInfo get(PatchSet.Id patchSetId)
- throws PatchSetInfoNotAvailableException {
- ReviewDb db = null;
- Repository repo = null;
- try {
- db = schemaFactory.open();
- final PatchSet patchSet = db.patchSets().get(patchSetId);
- final Change change = db.changes().get(patchSet.getId().getParentKey());
- final Project.NameKey projectKey = change.getProject();
- final String projectName = projectKey.get();
- repo = repoManager.openRepository(projectName);
- final RevWalk rw = new RevWalk(repo);
- final RevCommit src =
- rw.parseCommit(ObjectId.fromString(patchSet.getRevision().get()));
- return get(src, patchSetId);
- } catch (OrmException e) {
- throw new PatchSetInfoNotAvailableException(e);
- } catch (IOException e) {
- throw new PatchSetInfoNotAvailableException(e);
- } finally {
- if (db != null) {
- db.close();
- }
- if (repo != null) {
- repo.close();
- }
- }
- }
-
- private UserIdentity toUserIdentity(final PersonIdent who) {
- final UserIdentity u = new UserIdentity();
- u.setName(who.getName());
- u.setEmail(who.getEmailAddress());
- u.setDate(new Timestamp(who.getWhen().getTime()));
- u.setTimeZone(who.getTimeZoneOffset());
-
- // If only one account has access to this email address, select it
- // as the identity of the user.
- //
- final Set<Account.Id> a = byEmailCache.get(u.getEmail());
- if (a.size() == 1) {
- u.setAccount(a.iterator().next());
- }
-
- return u;
- }
-
-}
diff --git a/src/main/java/com/google/gerrit/server/patch/Text.java b/src/main/java/com/google/gerrit/server/patch/Text.java
deleted file mode 100644
index c5c3ecb376..0000000000
--- a/src/main/java/com/google/gerrit/server/patch/Text.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import com.google.gerrit.client.data.SparseFileContent;
-
-import org.eclipse.jgit.diff.RawText;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.util.RawParseUtils;
-
-public class Text extends RawText {
- public static final Text EMPTY = new Text(new byte[0]);
-
- public Text(final byte[] r) {
- super(r);
- }
-
- public byte[] getContent() {
- return content;
- }
-
- public String getLine(final int i) {
- final int s = lines.get(i + 1);
- int e = lines.get(i + 2);
- if (content[e - 1] == '\n') {
- e--;
- }
- return RawParseUtils.decode(Constants.CHARSET, content, s, e);
- }
-
- public void addLineTo(final SparseFileContent out, final int i) {
- out.addLine(i, getLine(i));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/src/main/java/com/google/gerrit/server/project/ChangeControl.java
deleted file mode 100644
index 6df9b0c91e..0000000000
--- a/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-/** Access control management for a user accessing a single change. */
-public class ChangeControl {
- public static class Factory {
- private final ProjectControl.Factory projectControl;
- private final Provider<ReviewDb> db;
-
- @Inject
- Factory(final ProjectControl.Factory p, final Provider<ReviewDb> d) {
- projectControl = p;
- db = d;
- }
-
- public ChangeControl controlFor(final Change.Id id)
- throws NoSuchChangeException {
- final Change change;
- try {
- change = db.get().changes().get(id);
- if (change == null) {
- throw new NoSuchChangeException(id);
- }
- } catch (OrmException e) {
- throw new NoSuchChangeException(id, e);
- }
- return controlFor(change);
- }
-
- public ChangeControl controlFor(final Change change)
- throws NoSuchChangeException {
- try {
- final Project.NameKey projectKey = change.getProject();
- return projectControl.validateFor(projectKey).controlFor(change);
- } catch (NoSuchProjectException e) {
- throw new NoSuchChangeException(change.getId(), e);
- }
- }
-
- public ChangeControl validateFor(final Change.Id id)
- throws NoSuchChangeException {
- return validate(controlFor(id));
- }
-
- public ChangeControl validateFor(final Change change)
- throws NoSuchChangeException {
- return validate(controlFor(change));
- }
-
- private static ChangeControl validate(final ChangeControl c)
- throws NoSuchChangeException {
- if (!c.isVisible()) {
- throw new NoSuchChangeException(c.getChange().getId());
- }
- return c;
- }
- }
-
- private final ProjectControl projectControl;
- private final Change change;
-
- ChangeControl(final ProjectControl p, final Change c) {
- this.projectControl = p;
- this.change = c;
- }
-
- public ChangeControl forAnonymousUser() {
- return new ChangeControl(projectControl.forAnonymousUser(), change);
- }
-
- public ChangeControl forUser(final CurrentUser who) {
- return new ChangeControl(projectControl.forUser(who), change);
- }
-
- public CurrentUser getCurrentUser() {
- return getProjectControl().getCurrentUser();
- }
-
- public ProjectControl getProjectControl() {
- return projectControl;
- }
-
- public Project getProject() {
- return getProjectControl().getProject();
- }
-
- public Change getChange() {
- return change;
- }
-
- /** Can this user see this change? */
- public boolean isVisible() {
- return getProjectControl().isVisible();
- }
-
- /** Can this user abandon this change? */
- public boolean canAbandon() {
- return isOwner() // owner (aka creator) of the change can abandon
- || getProjectControl().isOwner() // project owner can abandon
- || getCurrentUser().isAdministrator() // site administers are god
- ;
- }
-
- /** Is this user the owner of the change? */
- public boolean isOwner() {
- if (getCurrentUser() instanceof IdentifiedUser) {
- final IdentifiedUser i = (IdentifiedUser) getCurrentUser();
- return i.getAccountId().equals(change.getOwner());
- }
- return false;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java b/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java
deleted file mode 100644
index 00c19c954b..0000000000
--- a/src/main/java/com/google/gerrit/server/project/NoSuchChangeException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.Change;
-
-/** Indicates the change does not exist. */
-public class NoSuchChangeException extends Exception {
- public NoSuchChangeException(final Change.Id key) {
- this(key, null);
- }
-
- public NoSuchChangeException(final Change.Id key, final Throwable why) {
- super(key.toString(), why);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java b/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java
deleted file mode 100644
index 55c7de3155..0000000000
--- a/src/main/java/com/google/gerrit/server/project/NoSuchProjectException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.Project;
-
-/** Indicates the project does not exist. */
-public class NoSuchProjectException extends Exception {
- public NoSuchProjectException(final Project.NameKey key) {
- this(key, null);
- }
-
- public NoSuchProjectException(final Project.NameKey key, final Throwable why) {
- super(key.toString(), why);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/src/main/java/com/google/gerrit/server/project/ProjectCache.java
deleted file mode 100644
index 19e174a33f..0000000000
--- a/src/main/java/com/google/gerrit/server/project/ProjectCache.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.Project;
-
-/** Cache of project information, including access rights. */
-public interface ProjectCache {
- /**
- * Get the cached data for a project by its unique name.
- *
- * @param projectName name of the project.
- * @return the cached data; null if no such project exists.
- */
- public ProjectState get(Project.NameKey projectName);
-
- /** Invalidate the cached information about the given project. */
- public void evict(Project p);
-}
diff --git a/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
deleted file mode 100644
index 5e5b97d0ac..0000000000
--- a/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-import java.util.Collection;
-import java.util.Collections;
-
-/** Cache of project information, including access rights. */
-@Singleton
-public class ProjectCacheImpl implements ProjectCache {
- private static final String CACHE_NAME = "projects";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<Project.NameKey, ProjectState>> type =
- new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {};
- core(type, CACHE_NAME);
- bind(ProjectCacheImpl.class);
- bind(ProjectCache.class).to(ProjectCacheImpl.class);
- }
- };
- }
-
- private final ProjectState.Factory projectStateFactory;
- private final Project.NameKey wildProject;
- private final ProjectState.InheritedRights inheritedRights;
- private final SchemaFactory<ReviewDb> schema;
- private final SelfPopulatingCache<Project.NameKey, ProjectState> byName;
-
- @Inject
- ProjectCacheImpl(final ProjectState.Factory psf,
- final SchemaFactory<ReviewDb> sf,
- @WildProjectName final Project.NameKey wp,
- @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName) {
- projectStateFactory = psf;
- schema = sf;
- wildProject = wp;
-
- this.byName =
- new SelfPopulatingCache<Project.NameKey, ProjectState>(byName) {
- @Override
- public ProjectState createEntry(final Project.NameKey key)
- throws Exception {
- return lookup(key);
- }
- };
-
- this.inheritedRights = new ProjectState.InheritedRights() {
- @Override
- public Collection<ProjectRight> get() {
- return ProjectCacheImpl.this.get(wildProject).getLocalRights();
- }
- };
- }
-
- private ProjectState lookup(final Project.NameKey key) throws OrmException {
- final ReviewDb db = schema.open();
- try {
- final Project p = db.projects().get(key);
- if (p == null) {
- return null;
- }
-
- final Collection<ProjectRight> rights =
- Collections.unmodifiableCollection(db.projectRights().byProject(
- p.getNameKey()).toList());
-
- return projectStateFactory.create(p, rights, inheritedRights);
- } finally {
- db.close();
- }
- }
-
- /**
- * Get the cached data for a project by its unique name.
- *
- * @param projectName name of the project.
- * @return the cached data; null if no such project exists.
- */
- public ProjectState get(final Project.NameKey projectName) {
- return byName.get(projectName);
- }
-
- /** Invalidate the cached information about the given project. */
- public void evict(final Project p) {
- if (p != null) {
- byName.remove(p.getNameKey());
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/src/main/java/com/google/gerrit/server/project/ProjectControl.java
deleted file mode 100644
index dcd55b7237..0000000000
--- a/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD_CREATE;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_TAG;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.server.CurrentUser;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.eclipse.jgit.lib.Constants;
-
-import java.util.Set;
-
-/** Access control management for a user accessing a project's data. */
-public class ProjectControl {
- public static final int VISIBLE = 1 << 0;
- public static final int OWNER = 1 << 1;
-
- public static class Factory {
- private final ProjectCache projectCache;
- private final Provider<CurrentUser> user;
-
- @Inject
- Factory(final ProjectCache pc, final Provider<CurrentUser> cu) {
- projectCache = pc;
- user = cu;
- }
-
- public ProjectControl controlFor(final Project.NameKey nameKey)
- throws NoSuchProjectException {
- final ProjectState p = projectCache.get(nameKey);
- if (p == null) {
- throw new NoSuchProjectException(nameKey);
- }
- return p.controlFor(user.get());
- }
-
- public ProjectControl validateFor(final Project.NameKey nameKey)
- throws NoSuchProjectException {
- return validateFor(nameKey, VISIBLE);
- }
-
- public ProjectControl ownerFor(final Project.NameKey nameKey)
- throws NoSuchProjectException {
- return validateFor(nameKey, OWNER);
- }
-
- public ProjectControl validateFor(final Project.NameKey nameKey,
- final int need) throws NoSuchProjectException {
- final ProjectControl c = controlFor(nameKey);
- if ((need & VISIBLE) == VISIBLE && c.isVisible()) {
- return c;
- }
- if ((need & OWNER) == OWNER && c.isOwner()) {
- return c;
- }
- throw new NoSuchProjectException(nameKey);
- }
- }
-
- private final CurrentUser user;
- private final ProjectState state;
-
- ProjectControl(final CurrentUser who, final ProjectState ps) {
- user = who;
- state = ps;
- }
-
- public ProjectControl forAnonymousUser() {
- return state.controlForAnonymousUser();
- }
-
- public ProjectControl forUser(final CurrentUser who) {
- return state.controlFor(who);
- }
-
- public ChangeControl controlFor(final Change change) {
- return new ChangeControl(this, change);
- }
-
- public CurrentUser getCurrentUser() {
- return user;
- }
-
- public ProjectState getProjectState() {
- return state;
- }
-
- public Project getProject() {
- return getProjectState().getProject();
- }
-
- /** Can this user see this project exists? */
- public boolean isVisible() {
- return canPerform(ApprovalCategory.READ, (short) 1);
- }
-
- /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
- public boolean isOwner() {
- return canPerform(ApprovalCategory.OWN, (short) 1)
- || getCurrentUser().isAdministrator();
- }
-
- /** Can this user create the given ref through this access path? */
- public boolean canCreateRef(final String refname) {
- switch (user.getAccessPath()) {
- case WEB:
- if (isOwner()) {
- return true;
- }
- if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_CREATE)) {
- return true;
- }
- return false;
-
- case SSH:
- if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_CREATE)) {
- return true;
- }
- if (isTag(refname) && canPerform(PUSH_TAG, (short) 1)) {
- return true;
- }
- return false;
-
- default:
- return false;
- }
- }
-
- /** Can this user delete the given ref through this access path? */
- public boolean canDeleteRef(final String refname) {
- switch (user.getAccessPath()) {
- case WEB:
- if (isOwner()) {
- return true;
- }
- if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
- return true;
- }
- return false;
-
- case SSH:
- if (isHead(refname) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
- return true;
- }
- return false;
-
- default:
- return false;
- }
- }
-
- /**
- * Can this user perform the action in this project, at the level asked?
- * <p>
- * This method checks the project rights against the user's effective groups.
- * If no right for the given category was granted to any of the user's
- * effective groups, then the rights from the wildcard project are checked.
- *
- * @param actionId unique action id.
- * @param requireValue minimum value the application needs to perform this
- * action.
- * @return true if the action can be performed; false if the user lacks the
- * necessary permission.
- */
- public boolean canPerform(final ApprovalCategory.Id actionId,
- final short requireValue) {
- final Set<AccountGroup.Id> groups = user.getEffectiveGroups();
- int val = Integer.MIN_VALUE;
- for (final ProjectRight pr : state.getLocalRights()) {
- if (actionId.equals(pr.getApprovalCategoryId())
- && groups.contains(pr.getAccountGroupId())) {
- if (val < 0 && pr.getMaxValue() > 0) {
- // If one of the user's groups had denied them access, but
- // this group grants them access, prefer the grant over
- // the denial. We have to break the tie somehow and we
- // prefer being "more open" to being "more closed".
- //
- val = pr.getMaxValue();
- } else {
- // Otherwise we use the largest value we can get.
- //
- val = Math.max(pr.getMaxValue(), val);
- }
- }
- }
- if (val == Integer.MIN_VALUE && actionId.canInheritFromWildProject()) {
- for (final ProjectRight pr : state.getInheritedRights()) {
- if (actionId.equals(pr.getApprovalCategoryId())
- && groups.contains(pr.getAccountGroupId())) {
- val = Math.max(pr.getMaxValue(), val);
- }
- }
- }
-
- return val >= requireValue;
- }
-
- private static boolean isHead(final String refname) {
- return refname.startsWith(Constants.R_HEADS);
- }
-
- private static boolean isTag(final String refname) {
- return refname.startsWith(Constants.R_TAGS);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/project/ProjectState.java b/src/main/java/com/google/gerrit/server/project/ProjectState.java
deleted file mode 100644
index f6a168d309..0000000000
--- a/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.project;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.server.AnonymousUser;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/** Cached information on a project. */
-public class ProjectState {
- public interface Factory {
- ProjectState create(Project project, Collection<ProjectRight> localRights,
- InheritedRights inheritedRights);
- }
-
- public interface InheritedRights {
- Collection<ProjectRight> get();
- }
-
- private final AnonymousUser anonymousUser;
- private final Project.NameKey wildProject;
-
- private final Project project;
- private final Collection<ProjectRight> localRights;
- private final InheritedRights inheritedRights;
- private final Set<AccountGroup.Id> owners;
-
- @Inject
- protected ProjectState(final AnonymousUser anonymousUser,
- @WildProjectName final Project.NameKey wildProject,
- @Assisted final Project project,
- @Assisted final Collection<ProjectRight> rights,
- @Assisted final InheritedRights inheritedRights) {
- this.anonymousUser = anonymousUser;
- this.wildProject = wildProject;
-
- this.project = project;
- this.localRights = rights;
- this.inheritedRights = inheritedRights;
-
- final HashSet<AccountGroup.Id> groups = new HashSet<AccountGroup.Id>();
- for (final ProjectRight right : rights) {
- if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId())
- && right.getMaxValue() > 0) {
- groups.add(right.getAccountGroupId());
- }
- }
- owners = Collections.unmodifiableSet(groups);
- }
-
- public Project getProject() {
- return project;
- }
-
- /** Get the rights that pertain only to this project. */
- public Collection<ProjectRight> getLocalRights() {
- return localRights;
- }
-
- /** Get the rights this project inherits from the wild project. */
- public Collection<ProjectRight> getInheritedRights() {
- if (isSpecialWildProject()) {
- return Collections.emptyList();
- }
- return inheritedRights.get();
- }
-
- /** Is this the special wild project which manages inherited rights? */
- public boolean isSpecialWildProject() {
- return project.getNameKey().equals(wildProject);
- }
-
- public Set<AccountGroup.Id> getOwners() {
- return owners;
- }
-
- public ProjectControl controlForAnonymousUser() {
- return controlFor(anonymousUser);
- }
-
- public ProjectControl controlFor(final CurrentUser user) {
- return new ProjectControl(user, this);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java b/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java
deleted file mode 100644
index 2b4a6b95e2..0000000000
--- a/src/main/java/com/google/gerrit/server/query/ChangeQueryBuilder.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.query;
-
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.inject.Singleton;
-
-import org.eclipse.jgit.lib.AbbreviatedObjectId;
-
-/**
- * Parses a query string meant to be applied to change objects.
- * <p>
- * This class is thread-safe, and may be reused across threads to parse queries.
- */
-@Singleton
-public class ChangeQueryBuilder extends QueryBuilder {
- public static final String FIELD_CHANGE = "change";
- public static final String FIELD_COMMIT = "commit";
- public static final String FIELD_REVIEWER = "reviewer";
- public static final String FIELD_OWNER = "owner";
-
- private static final String CHANGE_RE = "^[1-9][0-9]*$";
- private static final String COMMIT_RE =
- "^([0-9a-fA-F]{4," + RevId.LEN + "})$";
-
- @Operator
- public Predicate change(final String value) {
- match(value, CHANGE_RE);
- return new OperatorPredicate(FIELD_CHANGE, value);
- }
-
- @Operator
- public Predicate commit(final String value) {
- final AbbreviatedObjectId id = AbbreviatedObjectId.fromString(value);
- return new ObjectIdPredicate(FIELD_COMMIT, id);
- }
-
- @Operator
- public Predicate owner(final String value) {
- return new OperatorPredicate(FIELD_OWNER, value);
- }
-
- @Operator
- public Predicate reviewer(final String value) {
- return new OperatorPredicate(FIELD_REVIEWER, value);
- }
-
- @Override
- protected Predicate defaultField(final String value)
- throws QueryParseException {
- if (value.matches(CHANGE_RE)) {
- return change(value);
-
- } else if (value.matches(COMMIT_RE)) {
- return commit(value);
-
- } else {
- throw error("Unsupported query:" + value);
- }
- }
-
- private static void match(String val, String re) {
- if (!val.matches(re)) {
- throw new IllegalArgumentException("Invalid value :" + val);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/ChangeListServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/ChangeListServiceImpl.java
deleted file mode 100644
index 88b5224cf2..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/ChangeListServiceImpl.java
+++ /dev/null
@@ -1,599 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc;
-
-import com.google.gerrit.client.changes.ChangeListService;
-import com.google.gerrit.client.changes.ToggleStarRequest;
-import com.google.gerrit.client.data.AccountDashboardInfo;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.data.SingleListChangeInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeAccess;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.StarredChange;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.Transaction;
-import com.google.gwtorm.client.impl.ListResultSet;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ChangeListServiceImpl extends BaseServiceImplementation implements
- ChangeListService {
- private static final Comparator<ChangeInfo> ID_COMP =
- new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o1.getId().get() - o2.getId().get();
- }
- };
- private static final Comparator<ChangeInfo> SORT_KEY_COMP =
- new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o2.getSortKey().compareTo(o1.getSortKey());
- }
- };
- private static final Comparator<Change> QUERY_PREV =
- new Comparator<Change>() {
- public int compare(final Change a, final Change b) {
- return a.getSortKey().compareTo(b.getSortKey());
- }
- };
- private static final Comparator<Change> QUERY_NEXT =
- new Comparator<Change>() {
- public int compare(final Change a, final Change b) {
- return b.getSortKey().compareTo(a.getSortKey());
- }
- };
-
- private static final int MAX_PER_PAGE = 100;
-
- private static int safePageSize(final int pageSize) {
- return 0 < pageSize && pageSize <= MAX_PER_PAGE ? pageSize : MAX_PER_PAGE;
- }
-
- private final Provider<CurrentUser> currentUser;
- private final ChangeControl.Factory changeControlFactory;
- private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
-
- @Inject
- ChangeListServiceImpl(final Provider<ReviewDb> schema,
- final Provider<CurrentUser> currentUser,
- final ChangeControl.Factory changeControlFactory,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory) {
- super(schema, currentUser);
- this.currentUser = currentUser;
- this.changeControlFactory = changeControlFactory;
- this.accountInfoCacheFactory = accountInfoCacheFactory;
- }
-
- private boolean canRead(final Change c) {
- try {
- return changeControlFactory.controlFor(c).isVisible();
- } catch (NoSuchChangeException e) {
- return false;
- }
- }
-
- public void allOpenPrev(final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().allOpenPrev(sortKey, slim);
- }
- });
- }
-
- public void allOpenNext(final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().allOpenNext(sortKey, slim);
- }
- });
- }
-
- public void byProjectOpenPrev(final Project.NameKey project,
- final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().byProjectOpenPrev(project, sortKey, slim);
- }
- });
- }
-
- public void byProjectOpenNext(final Project.NameKey project,
- final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().byProjectOpenNext(project, sortKey, slim);
- }
- });
- }
-
- public void byProjectClosedPrev(final Project.NameKey project,
- final Change.Status s, final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().byProjectClosedPrev(s.getCode(), project, sortKey,
- slim);
- }
- });
- }
-
- public void byProjectClosedNext(final Project.NameKey project,
- final Change.Status s, final String pos, final int pageSize,
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
- throws OrmException {
- return db.changes().byProjectClosedNext(s.getCode(), project, sortKey,
- slim);
- }
- });
- }
-
- public void allClosedPrev(final Change.Status s, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException {
- return db.changes().allClosedPrev(s.getCode(), key, lim);
- }
- });
- }
-
- public void allClosedNext(final Change.Status s, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException {
- return db.changes().allClosedNext(s.getCode(), key, lim);
- }
- });
- }
-
- @Override
- public void allQueryPrev(final String query, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryPrev(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException {
- return searchQuery(db, query, lim, key, QUERY_PREV);
- }
- });
- }
-
- @Override
- public void allQueryNext(final String query, final String pos,
- final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new QueryNext(pageSize, pos) {
- @Override
- ResultSet<Change> query(ReviewDb db, int lim, String key)
- throws OrmException {
- return searchQuery(db, query, lim, key, QUERY_NEXT);
- }
- });
- }
-
- private ResultSet<Change> searchQuery(final ReviewDb db, String query,
- final int limit, final String key, final Comparator<Change> cmp)
- throws OrmException {
- List<Change> result = new ArrayList<Change>();
- final HashSet<Change.Id> want = new HashSet<Change.Id>();
- query = query.trim();
-
- if (query.matches("^[1-9][0-9]*$")) {
- want.add(Change.Id.parse(query));
-
- } else if (query.matches("^[iI][0-9a-f]{4,}.*$")) {
- if (query.startsWith("i")) {
- query = "I" + query.substring(1);
- }
- final Change.Key a = new Change.Key(query);
- final Change.Key b = a.max();
- filterBySortKey(result, db.changes().byKeyRange(a, b), cmp, key);
- Collections.sort(result, cmp);
- if (limit < result.size()) {
- result = result.subList(0, limit);
- }
-
- } else if (query.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
- final RevId id = new RevId(query);
- final ResultSet<PatchSet> patches;
- if (id.isComplete()) {
- patches = db.patchSets().byRevision(id);
- } else {
- patches = db.patchSets().byRevisionRange(id, id.max());
- }
- for (PatchSet p : patches) {
- want.add(p.getId().getParentKey());
- }
- } else if (query.contains("owner:")) {
- String[] parsedQuery = query.split(":");
- if (parsedQuery.length > 1) {
- filterBySortKey(result, changesCreatedBy(db, parsedQuery[1]), cmp, key);
- }
- } else if (query.contains("reviewer:")) {
- String[] parsedQuery = query.split(":");
- if (parsedQuery.length > 1) {
- want.addAll(changesReviewedBy(db, parsedQuery[1]));
- }
- }
-
- if (result.isEmpty() && want.isEmpty()) {
- return new ListResultSet<Change>(Collections.<Change> emptyList());
- }
-
- filterBySortKey(result, db.changes().get(want), cmp, key);
- Collections.sort(result, cmp);
- if (limit < result.size()) {
- result = result.subList(0, limit);
- }
- return new ListResultSet<Change>(result);
- }
-
- private static void filterBySortKey(final List<Change> dst,
- final Iterable<Change> src, final Comparator<Change> cmp, final String key) {
- if (cmp == QUERY_PREV) {
- for (Change c : src) {
- if (c.getSortKey().compareTo(key) > 0) {
- dst.add(c);
- }
- }
- } else /* cmp == QUERY_NEXT */{
- for (Change c : src) {
- if (c.getSortKey().compareTo(key) < 0) {
- dst.add(c);
- }
- }
- }
- }
-
- public void forAccount(final Account.Id id,
- final AsyncCallback<AccountDashboardInfo> callback) {
- final Account.Id me = getAccountId();
- final Account.Id target = id != null ? id : me;
- if (target == null) {
- callback.onFailure(new NoSuchEntityException());
- return;
- }
-
- run(callback, new Action<AccountDashboardInfo>() {
- public AccountDashboardInfo run(final ReviewDb db) throws OrmException,
- Failure {
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final Account user = ac.get(target);
- if (user == null) {
- throw new Failure(new NoSuchEntityException());
- }
-
- final Set<Change.Id> stars = currentUser.get().getStarredChanges();
- final ChangeAccess changes = db.changes();
- final AccountDashboardInfo d;
-
- final Set<Change.Id> openReviews = new HashSet<Change.Id>();
- final Set<Change.Id> closedReviews = new HashSet<Change.Id>();
- for (final PatchSetApproval ca : db.patchSetApprovals().openByUser(id)) {
- openReviews.add(ca.getPatchSetId().getParentKey());
- }
- for (final PatchSetApproval ca : db.patchSetApprovals()
- .closedByUser(id)) {
- closedReviews.add(ca.getPatchSetId().getParentKey());
- }
-
- d = new AccountDashboardInfo(target);
- d.setByOwner(filter(changes.byOwnerOpen(target), stars, ac));
- d.setClosed(filter(changes.byOwnerClosed(target), stars, ac));
-
- for (final ChangeInfo c : d.getByOwner()) {
- openReviews.remove(c.getId());
- }
- d.setForReview(filter(changes.get(openReviews), stars, ac));
- Collections.sort(d.getForReview(), ID_COMP);
-
- for (final ChangeInfo c : d.getClosed()) {
- closedReviews.remove(c.getId());
- }
- if (!closedReviews.isEmpty()) {
- d.getClosed().addAll(filter(changes.get(closedReviews), stars, ac));
- Collections.sort(d.getClosed(), SORT_KEY_COMP);
- }
-
- d.setAccounts(ac.create());
- return d;
- }
- });
- }
-
- public void myStarredChanges(
- final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new Action<SingleListChangeInfo>() {
- public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
- final Account.Id me = getAccountId();
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final SingleListChangeInfo d = new SingleListChangeInfo();
- final Set<Change.Id> starred = currentUser.get().getStarredChanges();
- d.setChanges(filter(db.changes().get(starred), starred, ac));
- Collections.sort(d.getChanges(), new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o1.getLastUpdatedOn().compareTo(o2.getLastUpdatedOn());
- }
- });
- d.setAccounts(ac.create());
- return d;
- }
- });
- }
-
- public void myDraftChanges(final AsyncCallback<SingleListChangeInfo> callback) {
- run(callback, new Action<SingleListChangeInfo>() {
- public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
- final Account.Id me = getAccountId();
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final SingleListChangeInfo d = new SingleListChangeInfo();
- final Set<Change.Id> starred = currentUser.get().getStarredChanges();
- final Set<Change.Id> drafted = draftedBy(db, me);
- d.setChanges(filter(db.changes().get(drafted), starred, ac));
- Collections.sort(d.getChanges(), new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o1.getLastUpdatedOn().compareTo(o2.getLastUpdatedOn());
- }
- });
- d.setAccounts(ac.create());
- return d;
- }
- });
- }
-
- public void toggleStars(final ToggleStarRequest req,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException {
- final Account.Id me = getAccountId();
- final Set<Change.Id> existing = currentUser.get().getStarredChanges();
- final ArrayList<StarredChange> add = new ArrayList<StarredChange>();
- final ArrayList<StarredChange> remove = new ArrayList<StarredChange>();
-
- if (req.getAddSet() != null) {
- for (final Change.Id id : req.getAddSet()) {
- if (!existing.contains(id)) {
- add.add(new StarredChange(new StarredChange.Key(me, id)));
- }
- }
- }
-
- if (req.getRemoveSet() != null) {
- for (final Change.Id id : req.getRemoveSet()) {
- if (existing.contains(id)) {
- remove.add(new StarredChange(new StarredChange.Key(me, id)));
- }
- }
- }
-
- if (!add.isEmpty() || !remove.isEmpty()) {
- final Transaction txn = db.beginTransaction();
- db.starredChanges().insert(add);
- db.starredChanges().delete(remove);
- txn.commit();
- }
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void myStarredChangeIds(final AsyncCallback<Set<Change.Id>> callback) {
- callback.onSuccess(currentUser.get().getStarredChanges());
- }
-
- private List<ChangeInfo> filter(final ResultSet<Change> rs,
- final Set<Change.Id> starred, final AccountInfoCacheFactory accts) {
- final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>();
- for (final Change c : rs) {
- if (canRead(c)) {
- final ChangeInfo ci = new ChangeInfo(c);
- accts.want(ci.getOwner());
- ci.setStarred(starred.contains(ci.getId()));
- r.add(ci);
- }
- }
- return r;
- }
-
- private static Set<Change.Id> draftedBy(final ReviewDb db, final Account.Id me)
- throws OrmException {
- final Set<Change.Id> existing = new HashSet<Change.Id>();
- if (me != null) {
- for (final PatchLineComment sc : db.patchComments().draftByAuthor(me)) {
- final Change.Id c =
- sc.getKey().getParentKey().getParentKey().getParentKey();
- existing.add(c);
- }
- }
- return existing;
- }
-
- /**
- * @return a set of all the account ID's matching the given user name in
- * either of the following columns: ssh name, email address, full name
- */
- private static Set<Account.Id> getAccountSources(final ReviewDb db,
- final String userName) throws OrmException {
- Set<Account.Id> result = new HashSet<Account.Id>();
- String a = userName;
- String b = userName + "\u9fa5";
- addAll(result, db.accounts().suggestBySshUserName(a, b, 10));
- addAll(result, db.accounts().suggestByFullName(a, b, 10));
- for (AccountExternalId extId : db.accountExternalIds()
- .suggestByEmailAddress(a, b, 10)) {
- result.add(extId.getAccountId());
- }
- return result;
- }
-
- private static void addAll(Set<Account.Id> result, ResultSet<Account> rs) {
- for (Account account : rs) {
- result.add(account.getId());
- }
- }
-
- /**
- * @return a set of all the changes created by userName. This method tries to
- * find userName in 1) the ssh user names, 2) the full names and 3)
- * the email addresses. The returned changes are unique and sorted by
- * time stamp, newer first.
- */
- private List<Change> changesCreatedBy(final ReviewDb db, final String userName)
- throws OrmException {
- final List<Change> resultChanges = new ArrayList<Change>();
- for (Account.Id account : getAccountSources(db, userName)) {
- for (Change change : db.changes().byOwnerOpen(account)) {
- resultChanges.add(change);
- }
- for (Change change : db.changes().byOwnerClosedAll(account)) {
- resultChanges.add(change);
- }
- }
- return resultChanges;
- }
-
- /**
- * @return a set of all the changes reviewed by userName. This method tries to
- * find userName in 1) the ssh user names, 2) the full names and the
- * email addresses. The returned changes are unique and sorted by time
- * stamp, newer first.
- */
- private Set<Change.Id> changesReviewedBy(final ReviewDb db,
- final String userName) throws OrmException {
- final Set<Change.Id> resultChanges = new HashSet<Change.Id>();
- for (Account.Id account : getAccountSources(db, userName)) {
- for (PatchSetApproval a : db.patchSetApprovals().openByUser(account)) {
- resultChanges.add(a.getPatchSetId().getParentKey());
- }
- for (PatchSetApproval a : db.patchSetApprovals().closedByUserAll(account)) {
- resultChanges.add(a.getPatchSetId().getParentKey());
- }
- }
- return resultChanges;
- }
-
- private abstract class QueryNext implements Action<SingleListChangeInfo> {
- protected final String pos;
- protected final int limit;
- protected final int slim;
-
- QueryNext(final int pageSize, final String pos) {
- this.pos = pos;
- this.limit = safePageSize(pageSize);
- this.slim = limit + 1;
- }
-
- public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
- final Account.Id me = getAccountId();
- final AccountInfoCacheFactory ac = accountInfoCacheFactory.create();
- final SingleListChangeInfo d = new SingleListChangeInfo();
- final Set<Change.Id> starred = currentUser.get().getStarredChanges();
-
- boolean results = true;
- String sortKey = pos;
- final ArrayList<ChangeInfo> list = new ArrayList<ChangeInfo>();
- while (results && list.size() < slim) {
- results = false;
- final ResultSet<Change> rs = query(db, slim, sortKey);
- for (final Change c : rs) {
- results = true;
- if (canRead(c)) {
- final ChangeInfo ci = new ChangeInfo(c);
- ac.want(ci.getOwner());
- ci.setStarred(starred.contains(ci.getId()));
- list.add(ci);
- if (list.size() == slim) {
- rs.close();
- break;
- }
- }
- sortKey = c.getSortKey();
- }
- }
-
- final boolean atEnd = finish(list);
- d.setChanges(list, atEnd);
- d.setAccounts(ac.create());
- return d;
- }
-
- boolean finish(final ArrayList<ChangeInfo> list) {
- final boolean atEnd = list.size() <= limit;
- if (list.size() == slim) {
- list.remove(limit);
- }
- return atEnd;
- }
-
- abstract ResultSet<Change> query(final ReviewDb db, final int slim,
- String sortKey) throws OrmException;
- }
-
- private abstract class QueryPrev extends QueryNext {
- QueryPrev(int pageSize, String pos) {
- super(pageSize, pos);
- }
-
- @Override
- boolean finish(final ArrayList<ChangeInfo> list) {
- final boolean atEnd = super.finish(list);
- Collections.reverse(list);
- return atEnd;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/Handler.java b/src/main/java/com/google/gerrit/server/rpc/Handler.java
deleted file mode 100644
index 831a0ae32e..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/Handler.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc;
-
-import com.google.gerrit.client.rpc.CorruptEntityException;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-
-import java.util.concurrent.Callable;
-
-/**
- * Base class for RPC service implementations.
- * <p>
- * Typically an RPC service implementation will extend this class and use Guice
- * injection to manage its state. For example:
- *
- * <pre>
- * class Foo extends Handler&lt;Result&gt; {
- * interface Factory {
- * Foo create(... args ...);
- * }
- * &#064;Inject
- * Foo(state, @Assisted args) { ... }
- * Result get() throws Exception { ... }
- * }
- * </pre>
- *
- * @param <T> type of result for {@link AsyncCallback#onSuccess(Object)} if the
- * operation completed successfully.
- */
-public abstract class Handler<T> implements Callable<T> {
- /**
- * Run the operation and pass the result to the callback.
- *
- * @param callback callback to receive the result of {@link #call()}.
- */
- public final void to(final AsyncCallback<T> callback) {
- try {
- final T r = call();
- if (r != null) {
- callback.onSuccess(r);
- }
- } catch (NoSuchProjectException e) {
- callback.onFailure(new NoSuchEntityException());
-
- } catch (NoSuchChangeException e) {
- callback.onFailure(new NoSuchEntityException());
-
- } catch (OrmException e) {
- if (e.getCause() instanceof BaseServiceImplementation.Failure) {
- callback.onFailure(e.getCause().getCause());
-
- } else if (e.getCause() instanceof CorruptEntityException) {
- callback.onFailure(e.getCause());
-
- } else if (e.getCause() instanceof NoSuchEntityException) {
- callback.onFailure(e.getCause());
-
- } else {
- callback.onFailure(e);
- }
- } catch (BaseServiceImplementation.Failure e) {
- callback.onFailure(e.getCause());
- } catch (Exception e) {
- callback.onFailure(e);
- }
- }
-
- /**
- * Compute the operation result.
- *
- * @return the result of the operation. Return {@link VoidResult#INSTANCE} if
- * there is no meaningful return value for the operation.
- * @throws Exception the operation failed. The caller will log the exception
- * and the stack trace, if it is worth logging on the server side.
- */
- public abstract T call() throws Exception;
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/SuggestServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/SuggestServiceImpl.java
deleted file mode 100644
index 7d601c01fe..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/SuggestServiceImpl.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc;
-
-import com.google.gerrit.client.data.AccountInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.ui.SuggestService;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-
-class SuggestServiceImpl extends BaseServiceImplementation implements
- SuggestService {
- private static final String MAX_SUFFIX = "\u9fa5";
-
- private final ProjectCache projectCache;
- private final AccountCache accountCache;
- private final Provider<CurrentUser> currentUser;
-
- @Inject
- SuggestServiceImpl(final Provider<ReviewDb> schema,
- final ProjectCache projectCache, final AccountCache accountCache,
- final Provider<CurrentUser> currentUser) {
- super(schema, currentUser);
- this.projectCache = projectCache;
- this.accountCache = accountCache;
- this.currentUser = currentUser;
- }
-
- public void suggestProjectNameKey(final String query, final int limit,
- final AsyncCallback<List<Project.NameKey>> callback) {
- run(callback, new Action<List<Project.NameKey>>() {
- public List<Project.NameKey> run(final ReviewDb db) throws OrmException {
- final String a = query;
- final String b = a + MAX_SUFFIX;
- final int max = 10;
- final int n = limit <= 0 ? max : Math.min(limit, max);
-
- final CurrentUser user = currentUser.get();
- final List<Project.NameKey> r = new ArrayList<Project.NameKey>();
- for (final Project p : db.projects().suggestByName(a, b, n)) {
- final ProjectState e = projectCache.get(p.getNameKey());
- if (e != null && e.controlFor(user).isVisible()) {
- r.add(p.getNameKey());
- }
- }
- return r;
- }
- });
- }
-
- public void suggestAccount(final String query, final int limit,
- final AsyncCallback<List<AccountInfo>> callback) {
- run(callback, new Action<List<AccountInfo>>() {
- public List<AccountInfo> run(final ReviewDb db) throws OrmException {
- final String a = query;
- final String b = a + MAX_SUFFIX;
- final int max = 10;
- final int n = limit <= 0 ? max : Math.min(limit, max);
-
- final LinkedHashMap<Account.Id, AccountInfo> r =
- new LinkedHashMap<Account.Id, AccountInfo>();
- for (final Account p : db.accounts().suggestByFullName(a, b, n)) {
- r.put(p.getId(), new AccountInfo(p));
- }
- if (r.size() < n) {
- for (final Account p : db.accounts().suggestByPreferredEmail(a, b,
- n - r.size())) {
- r.put(p.getId(), new AccountInfo(p));
- }
- }
- if (r.size() < n) {
- for (final AccountExternalId e : db.accountExternalIds()
- .suggestByEmailAddress(a, b, n - r.size())) {
- if (!r.containsKey(e.getAccountId())) {
- final Account p = accountCache.get(e.getAccountId()).getAccount();
- final AccountInfo info = new AccountInfo(p);
- info.setPreferredEmail(e.getEmailAddress());
- r.put(e.getAccountId(), info);
- }
- }
- }
- return new ArrayList<AccountInfo>(r.values());
- }
- });
- }
-
- public void suggestAccountGroup(final String query, final int limit,
- final AsyncCallback<List<AccountGroup>> callback) {
- run(callback, new Action<List<AccountGroup>>() {
- public List<AccountGroup> run(final ReviewDb db) throws OrmException {
- final String a = query;
- final String b = a + MAX_SUFFIX;
- final int max = 10;
- final int n = limit <= 0 ? max : Math.min(limit, max);
- return db.accountGroups().suggestByName(a, b, n).toList();
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/SystemInfoServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/SystemInfoServiceImpl.java
deleted file mode 100644
index 799e252b64..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/SystemInfoServiceImpl.java
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc;
-
-import com.google.gerrit.client.data.SshHostKey;
-import com.google.gerrit.client.data.SystemInfoService;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.ssh.SshInfo;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import com.jcraft.jsch.HostKey;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
-import org.apache.sshd.common.util.Buffer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
-class SystemInfoServiceImpl implements SystemInfoService {
- private static final Logger log =
- LoggerFactory.getLogger(SystemInfoServiceImpl.class);
- private static final JSch JSCH = new JSch();
-
- private final SchemaFactory<ReviewDb> schema;
- private final SshInfo sshd;
- private final List<PublicKey> hostKeys;
- private final Provider<HttpServletRequest> httpRequest;
-
- @Inject
- SystemInfoServiceImpl(final SchemaFactory<ReviewDb> sf, final SshInfo daemon,
- final Provider<HttpServletRequest> hsr) {
- schema = sf;
- sshd = daemon;
- hostKeys = sortHostKeys();
- httpRequest = hsr;
- }
-
- private static boolean isIPv6(final InetAddress ip) {
- return ip instanceof Inet6Address
- && ip.getHostName().equals(ip.getHostAddress());
- }
-
- public void contributorAgreements(
- final AsyncCallback<List<ContributorAgreement>> callback) {
- try {
- final ReviewDb db = schema.open();
- try {
- callback.onSuccess(db.contributorAgreements().active().toList());
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- callback.onFailure(e);
- }
- }
-
- public void daemonHostKeys(final AsyncCallback<List<SshHostKey>> callback) {
- final String hostIdent = hostIdent();
- if (hostIdent == null) {
- callback.onSuccess(Collections.<SshHostKey> emptyList());
- return;
- }
-
- final ArrayList<SshHostKey> r = new ArrayList<SshHostKey>(hostKeys.size());
- for (final PublicKey pub : hostKeys) {
- try {
- final HostKey hk = toHostKey(hostIdent, pub);
- r.add(new SshHostKey(hk.getHost(), hk.getType() + " " + hk.getKey(), hk
- .getFingerPrint(JSCH)));
- } catch (JSchException e) {
- log.error("Invalid host key", e);
- continue;
- }
- }
- callback.onSuccess(r);
- }
-
- private List<PublicKey> sortHostKeys() {
- final List<PublicKey> r = new ArrayList<PublicKey>(2);
- r.addAll(sshd.getHostKeys());
- Collections.sort(r, new Comparator<PublicKey>() {
- @Override
- public int compare(final PublicKey a, final PublicKey b) {
- if (a == b) {
- return 0;
- }
- if (a instanceof RSAPublicKey) {
- return -1;
- }
- if (a instanceof DSAPublicKey) {
- return 1;
- }
- return 0;
- }
- });
- return Collections.unmodifiableList(r);
- }
-
- private HostKey toHostKey(final String hostIdent, final PublicKey pub)
- throws JSchException {
- final Buffer buf = new Buffer();
- buf.putPublicKey(pub);
- final byte[] keyBin = buf.getCompactData();
- return new HostKey(hostIdent, keyBin);
- }
-
- private String hostIdent() {
- InetSocketAddress addr = sshd.getAddress();
- if (addr == null) {
- return null;
- }
-
- InetAddress ip = addr.getAddress();
- if (ip.isAnyLocalAddress()) {
- try {
- ip = InetAddress.getByName(httpRequest.get().getServerName());
- } catch (UnknownHostException e) {
- throw new RuntimeException(e);
- }
- addr = new InetSocketAddress(ip, addr.getPort());
- }
-
- if (addr.getPort() == 22 && !isIPv6(ip)) {
- return addr.getHostName();
- }
- return "[" + addr.getHostName() + "]:" + addr.getPort();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/UiRpcModule.java b/src/main/java/com/google/gerrit/server/rpc/UiRpcModule.java
deleted file mode 100644
index 2d671710dd..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/UiRpcModule.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc;
-
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.account.AccountModule;
-import com.google.gerrit.server.rpc.changedetail.ChangeModule;
-import com.google.gerrit.server.rpc.patch.PatchModule;
-import com.google.gerrit.server.rpc.project.ProjectModule;
-
-/** Registers servlets to answer RPCs from client UI. */
-public class UiRpcModule extends RpcServletModule {
- public static final String PREFIX = "/gerrit/rpc/";
-
- public UiRpcModule() {
- super(PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- rpc(ChangeListServiceImpl.class);
- rpc(SuggestServiceImpl.class);
- rpc(SystemInfoServiceImpl.class);
-
- install(new AccountModule());
- install(new ChangeModule());
- install(new PatchModule());
- install(new ProjectModule());
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/AccountModule.java b/src/main/java/com/google/gerrit/server/rpc/account/AccountModule.java
deleted file mode 100644
index ea56108688..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/AccountModule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-
-public class AccountModule extends RpcServletModule {
- public AccountModule() {
- super(UiRpcModule.PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- install(new FactoryModule() {
- @Override
- protected void configure() {
- factory(AgreementInfoFactory.Factory.class);
- factory(ExternalIdDetailFactory.Factory.class);
- factory(GroupDetailFactory.Factory.class);
- factory(MyGroupsFactory.Factory.class);
- }
- });
- rpc(AccountSecurityImpl.class);
- rpc(AccountServiceImpl.class);
- rpc(GroupAdminServiceImpl.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/AccountSecurityImpl.java b/src/main/java/com/google/gerrit/server/rpc/account/AccountSecurityImpl.java
deleted file mode 100644
index fed89b90a1..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/AccountSecurityImpl.java
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.account.AccountSecurity;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.reviewdb.ContactInformation;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.ContactInformationStoreException;
-import com.google.gerrit.client.rpc.InvalidSshKeyException;
-import com.google.gerrit.client.rpc.InvalidSshUserNameException;
-import com.google.gerrit.client.rpc.NameAlreadyUsedException;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountByEmailCache;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountException;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.AuthRequest;
-import com.google.gerrit.server.account.Realm;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.contact.ContactStore;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.mail.RegisterNewEmailSender;
-import com.google.gerrit.server.ssh.SshKeyCache;
-import com.google.gerrit.server.ssh.SshUtil;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtjsonrpc.server.ValidToken;
-import com.google.gwtjsonrpc.server.XsrfException;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.util.Base64;
-
-import java.io.UnsupportedEncodingException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.spec.InvalidKeySpecException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-class AccountSecurityImpl extends BaseServiceImplementation implements
- AccountSecurity {
-
- private static final Pattern SSH_USER_NAME_PATTERN = Pattern.compile(Account.SSH_USER_NAME_PATTERN);
-
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final ContactStore contactStore;
- private final AuthConfig authConfig;
- private final Realm realm;
- private final RegisterNewEmailSender.Factory registerNewEmailFactory;
- private final SshKeyCache sshKeyCache;
- private final AccountByEmailCache byEmailCache;
- private final AccountCache accountCache;
- private final AccountManager accountManager;
- private final boolean useContactInfo;
-
- private final ExternalIdDetailFactory.Factory externalIdDetailFactory;
- private final MyGroupsFactory.Factory myGroupsFactory;
-
- @Inject
- AccountSecurityImpl(final Provider<ReviewDb> schema,
- final Provider<CurrentUser> currentUser, final ContactStore cs,
- final AuthConfig ac, final Realm r,
- final RegisterNewEmailSender.Factory esf, final SshKeyCache skc,
- final AccountByEmailCache abec, final AccountCache uac,
- final AccountManager am,
- final ExternalIdDetailFactory.Factory externalIdDetailFactory,
- final MyGroupsFactory.Factory myGroupsFactory) {
- super(schema, currentUser);
- contactStore = cs;
- authConfig = ac;
- realm = r;
- registerNewEmailFactory = esf;
- sshKeyCache = skc;
- byEmailCache = abec;
- accountCache = uac;
- accountManager = am;
-
- useContactInfo = contactStore != null && contactStore.isEnabled();
-
- this.externalIdDetailFactory = externalIdDetailFactory;
- this.myGroupsFactory = myGroupsFactory;
- }
-
- public void mySshKeys(final AsyncCallback<List<AccountSshKey>> callback) {
- run(callback, new Action<List<AccountSshKey>>() {
- public List<AccountSshKey> run(ReviewDb db) throws OrmException {
- return db.accountSshKeys().byAccount(getAccountId()).toList();
- }
- });
- }
-
- public void addSshKey(final String keyText,
- final AsyncCallback<AccountSshKey> callback) {
- run(callback, new Action<AccountSshKey>() {
- public AccountSshKey run(final ReviewDb db) throws OrmException, Failure {
- int max = 0;
- final Account.Id me = getAccountId();
- for (final AccountSshKey k : db.accountSshKeys().byAccount(me)) {
- max = Math.max(max, k.getKey().get());
- }
-
- String keyStr = keyText;
- if (keyStr.startsWith("---- BEGIN SSH2 PUBLIC KEY ----")) {
- keyStr = SshUtil.toOpenSshPublicKey(keyStr);
- }
-
- final AccountSshKey newKey =
- new AccountSshKey(new AccountSshKey.Id(me, max + 1), keyStr);
- try {
- SshUtil.parse(newKey);
- } catch (NoSuchAlgorithmException e) {
- throw new Failure(new InvalidSshKeyException());
- } catch (InvalidKeySpecException e) {
- throw new Failure(new InvalidSshKeyException());
- } catch (NoSuchProviderException e) {
- log.error("Cannot parse SSH key", e);
- throw new Failure(new InvalidSshKeyException());
- }
- db.accountSshKeys().insert(Collections.singleton(newKey));
- uncacheSshKeys(me);
- return newKey;
- }
- });
- }
-
- public void deleteSshKeys(final Set<AccountSshKey.Id> ids,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final Account.Id me = getAccountId();
- for (final AccountSshKey.Id keyId : ids) {
- if (!me.equals(keyId.getParentKey()))
- throw new Failure(new NoSuchEntityException());
- }
-
- final List<AccountSshKey> k = db.accountSshKeys().get(ids).toList();
- if (!k.isEmpty()) {
- final Transaction txn = db.beginTransaction();
- db.accountSshKeys().delete(k, txn);
- txn.commit();
- uncacheSshKeys(me);
- }
-
- return VoidResult.INSTANCE;
- }
- });
- }
-
- private void uncacheSshKeys(final Account.Id me) {
- uncacheSshKeys(accountCache.get(me).getAccount().getSshUserName());
- }
-
- private void uncacheSshKeys(final String userName) {
- sshKeyCache.evict(userName);
- }
-
- @Override
- public void changeSshUserName(final String newName,
- final AsyncCallback<VoidResult> callback) {
- if (!realm.allowsEdit(Account.FieldName.SSH_USER_NAME)) {
- callback.onFailure(new NameAlreadyUsedException());
- return;
- }
-
- run(callback, new Action<VoidResult>() {
- @Override
- public VoidResult run(ReviewDb db) throws OrmException, Failure {
- final Account me = db.accounts().get(getAccountId());
- if (me == null) {
- throw new Failure(new NoSuchEntityException());
- }
- if (newName != null && !SSH_USER_NAME_PATTERN.matcher(newName).matches()) {
- throw new Failure(new InvalidSshUserNameException());
- }
- final Account other;
- if (newName != null) {
- other = db.accounts().bySshUserName(newName);
- } else {
- other = null;
- }
-
- if (other != null) {
- if (other.getId().equals(me.getId())) {
- return VoidResult.INSTANCE;
- } else {
- throw new Failure(new NameAlreadyUsedException());
- }
- }
-
- final String oldName = me.getSshUserName();
- me.setSshUserName(newName);
- db.accounts().update(Collections.singleton(me));
- uncacheSshKeys(oldName);
- uncacheSshKeys(newName);
- accountCache.evict(me.getId());
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void myExternalIds(AsyncCallback<List<AccountExternalId>> callback) {
- externalIdDetailFactory.create().to(callback);
- }
-
- @Override
- public void myGroups(final AsyncCallback<List<AccountGroup>> callback) {
- myGroupsFactory.create().to(callback);
- }
-
- public void deleteExternalIds(final Set<AccountExternalId.Key> keys,
- final AsyncCallback<Set<AccountExternalId.Key>> callback) {
- run(callback, new Action<Set<AccountExternalId.Key>>() {
- public Set<AccountExternalId.Key> run(final ReviewDb db)
- throws OrmException, Failure {
- // Determine the records we will allow the user to remove.
- //
- final Account.Id me = getAccountId();
- final Map<AccountExternalId.Key, AccountExternalId> all =
- db.accountExternalIds()
- .toMap(db.accountExternalIds().byAccount(me));
-
- // Don't permit deletes unless they are for our own account
- //
- for (final AccountExternalId.Key keyId : keys) {
- if (!all.containsKey(keyId))
- throw new Failure(new NoSuchEntityException());
- }
-
- final AccountExternalId mostRecent =
- AccountExternalId.mostRecent(all.values());
- final Set<AccountExternalId.Key> removed =
- new HashSet<AccountExternalId.Key>();
- final List<AccountExternalId> toDelete =
- new ArrayList<AccountExternalId>();
- for (final AccountExternalId.Key k : keys) {
- final AccountExternalId e = all.get(k);
- if (e == null) {
- // Its already gone, tell the client its gone
- //
- removed.add(k);
-
- } else if (e == mostRecent) {
- // Don't delete the most recently accessed identity; the
- // user might lock themselves out of the account.
- //
- continue;
-
- } else {
- toDelete.add(e);
- removed.add(e.getKey());
- }
- }
-
- if (!toDelete.isEmpty()) {
- final Transaction txn = db.beginTransaction();
- db.accountExternalIds().delete(toDelete, txn);
- txn.commit();
- accountCache.evict(me);
- for (AccountExternalId e : toDelete) {
- byEmailCache.evict(e.getEmailAddress());
- }
- }
-
- return removed;
- }
- });
- }
-
- public void updateContact(final String name, final String emailAddr,
- final ContactInformation info, final AsyncCallback<Account> callback) {
- run(callback, new Action<Account>() {
- public Account run(ReviewDb db) throws OrmException, Failure {
- final Account me = db.accounts().get(getAccountId());
- final String oldEmail = me.getPreferredEmail();
- if (realm.allowsEdit(Account.FieldName.FULL_NAME)) {
- me.setFullName(name != null && !name.isEmpty() ? name : null);
- }
- me.setPreferredEmail(emailAddr);
- if (useContactInfo) {
- if (ContactInformation.hasAddress(info)
- || (me.isContactFiled() && ContactInformation.hasData(info))) {
- me.setContactFiled();
- }
- if (ContactInformation.hasData(info)) {
- try {
- contactStore.store(me, info);
- } catch (ContactInformationStoreException e) {
- throw new Failure(e);
- }
- }
- }
- db.accounts().update(Collections.singleton(me));
- if (!eq(oldEmail, me.getPreferredEmail())) {
- byEmailCache.evict(oldEmail);
- byEmailCache.evict(me.getPreferredEmail());
- }
- accountCache.evict(me.getId());
- return me;
- }
- });
- }
-
- private static boolean eq(final String a, final String b) {
- if (a == null && b == null) {
- return true;
- }
- return a != null && a.equals(b);
- }
-
- public void enterAgreement(final ContributorAgreement.Id id,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final ContributorAgreement cla = db.contributorAgreements().get(id);
- if (cla == null || !cla.isActive()) {
- throw new Failure(new NoSuchEntityException());
- }
-
- final AccountAgreement a =
- new AccountAgreement(new AccountAgreement.Key(getAccountId(), id));
- if (cla.isAutoVerify()) {
- a.review(AccountAgreement.Status.VERIFIED, null);
- }
- db.accountAgreements().insert(Collections.singleton(a));
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void registerEmail(final String address,
- final AsyncCallback<VoidResult> cb) {
- try {
- final RegisterNewEmailSender sender;
- sender = registerNewEmailFactory.create(address);
- sender.send();
- cb.onSuccess(VoidResult.INSTANCE);
- } catch (EmailException e) {
- log.error("Cannot send email verification message to " + address, e);
- cb.onFailure(e);
- } catch (RuntimeException e) {
- log.error("Cannot send email verification message to " + address, e);
- cb.onFailure(e);
- }
- }
-
- public void validateEmail(final String token,
- final AsyncCallback<VoidResult> callback) {
- try {
- final ValidToken t =
- authConfig.getEmailRegistrationToken().checkToken(token, null);
- if (t == null || t.getData() == null || "".equals(t.getData())) {
- callback.onFailure(new IllegalStateException("Invalid token"));
- return;
- }
- final String newEmail = new String(Base64.decode(t.getData()), "UTF-8");
- if (!newEmail.contains("@")) {
- callback.onFailure(new IllegalStateException("Invalid token"));
- return;
- }
- accountManager.link(getAccountId(), AuthRequest.forEmail(newEmail));
- callback.onSuccess(VoidResult.INSTANCE);
- } catch (XsrfException e) {
- callback.onFailure(e);
- } catch (UnsupportedEncodingException e) {
- callback.onFailure(e);
- } catch (AccountException e) {
- callback.onFailure(e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/AccountServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/account/AccountServiceImpl.java
deleted file mode 100644
index b5bb239edc..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/AccountServiceImpl.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.account.AccountProjectWatchInfo;
-import com.google.gerrit.client.account.AccountService;
-import com.google.gerrit.client.account.AgreementInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.reviewdb.AccountProjectWatch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-
-class AccountServiceImpl extends BaseServiceImplementation implements
- AccountService {
- private final Provider<IdentifiedUser> currentUser;
- private final AccountCache accountCache;
- private final ProjectControl.Factory projectControlFactory;
- private final AgreementInfoFactory.Factory agreementInfoFactory;
-
- @Inject
- AccountServiceImpl(final Provider<ReviewDb> schema,
- final Provider<IdentifiedUser> identifiedUser,
- final AccountCache accountCache,
- final ProjectControl.Factory projectControlFactory,
- final AgreementInfoFactory.Factory agreementInfoFactory) {
- super(schema, identifiedUser);
- this.currentUser = identifiedUser;
- this.accountCache = accountCache;
- this.projectControlFactory = projectControlFactory;
- this.agreementInfoFactory = agreementInfoFactory;
- }
-
- public void myAccount(final AsyncCallback<Account> callback) {
- callback.onSuccess(currentUser.get().getAccount());
- }
-
- public void changePreferences(final AccountGeneralPreferences pref,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final Account a = db.accounts().get(getAccountId());
- if (a == null) {
- throw new Failure(new NoSuchEntityException());
- }
- a.setGeneralPreferences(pref);
- db.accounts().update(Collections.singleton(a));
- accountCache.evict(a.getId());
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void myProjectWatch(
- final AsyncCallback<List<AccountProjectWatchInfo>> callback) {
- run(callback, new Action<List<AccountProjectWatchInfo>>() {
- public List<AccountProjectWatchInfo> run(ReviewDb db) throws OrmException {
- final List<AccountProjectWatchInfo> r =
- new ArrayList<AccountProjectWatchInfo>();
-
- for (final AccountProjectWatch w : db.accountProjectWatches()
- .byAccount(getAccountId()).toList()) {
- final ProjectControl ctl;
- try {
- ctl = projectControlFactory.validateFor(w.getProjectNameKey());
- } catch (NoSuchProjectException e) {
- db.accountProjectWatches().delete(Collections.singleton(w));
- continue;
- }
- r.add(new AccountProjectWatchInfo(w, ctl.getProject()));
- }
- Collections.sort(r, new Comparator<AccountProjectWatchInfo>() {
- public int compare(final AccountProjectWatchInfo a,
- final AccountProjectWatchInfo b) {
- return a.getProject().getName().compareTo(b.getProject().getName());
- }
- });
- return r;
- }
- });
- }
-
- public void addProjectWatch(final String projectName,
- final AsyncCallback<AccountProjectWatchInfo> callback) {
- run(callback, new Action<AccountProjectWatchInfo>() {
- public AccountProjectWatchInfo run(ReviewDb db) throws OrmException,
- NoSuchProjectException {
- final Project.NameKey nameKey = new Project.NameKey(projectName);
- final ProjectControl ctl = projectControlFactory.validateFor(nameKey);
-
- final AccountProjectWatch watch =
- new AccountProjectWatch(
- new AccountProjectWatch.Key(((IdentifiedUser) ctl
- .getCurrentUser()).getAccountId(), nameKey));
- db.accountProjectWatches().insert(Collections.singleton(watch));
- return new AccountProjectWatchInfo(watch, ctl.getProject());
- }
- });
- }
-
- public void updateProjectWatch(final AccountProjectWatch watch,
- final AsyncCallback<VoidResult> callback) {
- if (!getAccountId().equals(watch.getAccountId())) {
- callback.onFailure(new NoSuchEntityException());
- return;
- }
-
- run(callback, new Action<VoidResult>() {
- public VoidResult run(ReviewDb db) throws OrmException {
- db.accountProjectWatches().update(Collections.singleton(watch));
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void deleteProjectWatches(final Set<AccountProjectWatch.Key> keys,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final Account.Id me = getAccountId();
- for (final AccountProjectWatch.Key keyId : keys) {
- if (!me.equals(keyId.getParentKey()))
- throw new Failure(new NoSuchEntityException());
- }
-
- final List<AccountProjectWatch> k =
- db.accountProjectWatches().get(keys).toList();
- if (!k.isEmpty()) {
- final Transaction txn = db.beginTransaction();
- db.accountProjectWatches().delete(k, txn);
- txn.commit();
- }
-
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void myAgreements(final AsyncCallback<AgreementInfo> callback) {
- agreementInfoFactory.create().to(callback);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/AgreementInfoFactory.java b/src/main/java/com/google/gerrit/server/rpc/account/AgreementInfoFactory.java
deleted file mode 100644
index bea6e821d2..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/AgreementInfoFactory.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.account.AgreementInfo;
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-class AgreementInfoFactory extends Handler<AgreementInfo> {
- interface Factory {
- AgreementInfoFactory create();
- }
-
- private final ReviewDb db;
- private final IdentifiedUser user;
-
- private AgreementInfo info;
-
- @Inject
- AgreementInfoFactory(final ReviewDb db, final IdentifiedUser user) {
- this.db = db;
- this.user = user;
- }
-
- @Override
- public AgreementInfo call() throws Exception {
- final List<AccountAgreement> userAccepted =
- db.accountAgreements().byAccount(user.getAccountId()).toList();
- final List<AccountGroupAgreement> groupAccepted =
- new ArrayList<AccountGroupAgreement>();
- for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
- groupAccepted.addAll(db.accountGroupAgreements().byGroup(groupId)
- .toList());
- }
-
- final Map<ContributorAgreement.Id, ContributorAgreement> agreements =
- new HashMap<ContributorAgreement.Id, ContributorAgreement>();
- for (final AccountAgreement a : userAccepted) {
- final ContributorAgreement.Id id = a.getAgreementId();
- if (!agreements.containsKey(id)) {
- agreements.put(id, db.contributorAgreements().get(id));
- }
- }
- for (final AccountGroupAgreement a : groupAccepted) {
- final ContributorAgreement.Id id = a.getAgreementId();
- if (!agreements.containsKey(id)) {
- agreements.put(id, db.contributorAgreements().get(id));
- }
- }
-
- info = new AgreementInfo();
- info.setUserAccepted(userAccepted);
- info.setGroupAccepted(groupAccepted);
- info.setAgreements(agreements);
- return info;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/ExternalIdDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/account/ExternalIdDetailFactory.java
deleted file mode 100644
index 3e46a0b72e..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/ExternalIdDetailFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-
-import java.util.Collections;
-import java.util.List;
-
-class ExternalIdDetailFactory extends Handler<List<AccountExternalId>> {
- interface Factory {
- ExternalIdDetailFactory create();
- }
-
- private final ReviewDb db;
- private final IdentifiedUser user;
- private final AuthConfig authConfig;
-
- @Inject
- ExternalIdDetailFactory(final ReviewDb db, final IdentifiedUser user,
- final AuthConfig authConfig) {
- this.db = db;
- this.user = user;
- this.authConfig = authConfig;
- }
-
- @Override
- public List<AccountExternalId> call() throws Exception {
- final List<AccountExternalId> ids =
- db.accountExternalIds().byAccount(user.getAccountId()).toList();
- for (final AccountExternalId e : ids) {
- e.setTrusted(authConfig.isIdentityTrustable(Collections.singleton(e)));
- }
- return ids;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/GroupAdminServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/account/GroupAdminServiceImpl.java
deleted file mode 100644
index 27ccfab1fd..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/GroupAdminServiceImpl.java
+++ /dev/null
@@ -1,363 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.admin.GroupAdminService;
-import com.google.gerrit.client.admin.GroupDetail;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.reviewdb.AccountGroupMemberAudit;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NameAlreadyUsedException;
-import com.google.gerrit.client.rpc.NoSuchAccountException;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.account.NoSuchGroupException;
-import com.google.gerrit.server.account.Realm;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class GroupAdminServiceImpl extends BaseServiceImplementation implements
- GroupAdminService {
- private final Provider<IdentifiedUser> identifiedUser;
- private final AccountCache accountCache;
- private final AccountResolver accountResolver;
- private final Realm accountRealm;
- private final GroupCache groupCache;
- private final GroupControl.Factory groupControlFactory;
- private final GroupDetailFactory.Factory groupDetailFactory;
-
- @Inject
- GroupAdminServiceImpl(final Provider<ReviewDb> schema,
- final Provider<IdentifiedUser> currentUser,
- final AccountCache accountCache, final AccountResolver accountResolver,
- final Realm accountRealm, final GroupCache groupCache,
- final GroupControl.Factory groupControlFactory,
- final GroupDetailFactory.Factory groupDetailFactory) {
- super(schema, currentUser);
- this.identifiedUser = currentUser;
- this.accountCache = accountCache;
- this.accountResolver = accountResolver;
- this.accountRealm = accountRealm;
- this.groupCache = groupCache;
- this.groupControlFactory = groupControlFactory;
- this.groupDetailFactory = groupDetailFactory;
- }
-
- public void ownedGroups(final AsyncCallback<List<AccountGroup>> callback) {
- run(callback, new Action<List<AccountGroup>>() {
- public List<AccountGroup> run(ReviewDb db) throws OrmException {
- final IdentifiedUser user = identifiedUser.get();
- final List<AccountGroup> result;
- if (user.isAdministrator()) {
- result = db.accountGroups().all().toList();
- } else {
- final HashSet<AccountGroup.Id> seen = new HashSet<AccountGroup.Id>();
- result = new ArrayList<AccountGroup>();
- for (final AccountGroup.Id myGroup : user.getEffectiveGroups()) {
- for (AccountGroup group : db.accountGroups().ownedByGroup(myGroup)) {
- final AccountGroup.Id id = group.getId();
- if (!seen.add(id)) {
- continue;
- }
- try {
- GroupControl c = groupControlFactory.controlFor(id);
- if (c.isOwner()) {
- result.add(c.getAccountGroup());
- }
- } catch (NoSuchGroupException e) {
- continue;
- }
- }
- }
- }
- Collections.sort(result, new Comparator<AccountGroup>() {
- public int compare(final AccountGroup a, final AccountGroup b) {
- return a.getName().compareTo(b.getName());
- }
- });
- return result;
- }
- });
- }
-
- public void createGroup(final String newName,
- final AsyncCallback<AccountGroup.Id> callback) {
- run(callback, new Action<AccountGroup.Id>() {
- public AccountGroup.Id run(final ReviewDb db) throws OrmException,
- Failure {
- final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(newName);
- if (db.accountGroups().get(nameKey) != null) {
- throw new Failure(new NameAlreadyUsedException());
- }
-
- final AccountGroup group =
- new AccountGroup(nameKey, new AccountGroup.Id(db
- .nextAccountGroupId()));
- group.setNameKey(nameKey);
- group.setType(AccountGroup.Type.INTERNAL);
- group.setDescription("");
-
- final Account.Id me = getAccountId();
- final AccountGroupMember m =
- new AccountGroupMember(
- new AccountGroupMember.Key(me, group.getId()));
-
- final Transaction txn = db.beginTransaction();
- db.accountGroups().insert(Collections.singleton(group), txn);
- db.accountGroupMembers().insert(Collections.singleton(m), txn);
- db.accountGroupMembersAudit().insert(
- Collections.singleton(new AccountGroupMemberAudit(m, me)), txn);
- txn.commit();
- accountCache.evict(m.getAccountId());
-
- return group.getId();
- }
- });
- }
-
- public void groupDetail(final AccountGroup.Id groupId,
- final AsyncCallback<GroupDetail> callback) {
- groupDetailFactory.create(groupId).to(callback);
- }
-
- public void changeGroupDescription(final AccountGroup.Id groupId,
- final String description, final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
- group.setDescription(description);
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void changeGroupOwner(final AccountGroup.Id groupId,
- final String newOwnerName, final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
-
- final AccountGroup owner =
- db.accountGroups().get(new AccountGroup.NameKey(newOwnerName));
- if (owner == null) {
- throw new Failure(new NoSuchEntityException());
- }
-
- group.setOwnerGroupId(owner.getId());
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void renameGroup(final AccountGroup.Id groupId, final String newName,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
-
- final AccountGroup.NameKey oldKey = group.getNameKey();
- final AccountGroup.NameKey newKey = new AccountGroup.NameKey(newName);
- if (!newKey.equals(oldKey)) {
- if (db.accountGroups().get(newKey) != null) {
- throw new Failure(new NameAlreadyUsedException());
- }
- group.setNameKey(newKey);
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- groupCache.evictAfterRename(oldKey);
- }
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void changeGroupType(final AccountGroup.Id groupId,
- final AccountGroup.Type newType, final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
- group.setType(newType);
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void changeExternalGroup(final AccountGroup.Id groupId,
- final AccountGroup.ExternalNameKey bindTo,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException, Failure {
- final AccountGroup group = db.accountGroups().get(groupId);
- assertAmGroupOwner(db, group);
- group.setExternalNameKey(bindTo);
- db.accountGroups().update(Collections.singleton(group));
- groupCache.evict(group);
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void searchExternalGroups(final String searchFilter,
- final AsyncCallback<List<AccountGroup.ExternalNameKey>> callback) {
- final ArrayList<AccountGroup.ExternalNameKey> matches =
- new ArrayList<AccountGroup.ExternalNameKey>(accountRealm
- .lookupGroups(searchFilter));
- Collections.sort(matches, new Comparator<AccountGroup.ExternalNameKey>() {
- @Override
- public int compare(AccountGroup.ExternalNameKey a,
- AccountGroup.ExternalNameKey b) {
- return a.get().compareTo(b.get());
- }
- });
- callback.onSuccess(matches);
- }
-
- public void addGroupMember(final AccountGroup.Id groupId,
- final String nameOrEmail, final AsyncCallback<GroupDetail> callback) {
- run(callback, new Action<GroupDetail>() {
- public GroupDetail run(ReviewDb db) throws OrmException, Failure,
- NoSuchGroupException {
- final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
- throw new Failure(new NameAlreadyUsedException());
- }
-
- final Account a = findAccount(nameOrEmail);
- if (!control.canAdd(a.getId())) {
- throw new Failure(new NoSuchEntityException());
- }
-
- final AccountGroupMember.Key key =
- new AccountGroupMember.Key(a.getId(), groupId);
- AccountGroupMember m = db.accountGroupMembers().get(key);
- if (m == null) {
- m = new AccountGroupMember(key);
- final Transaction txn = db.beginTransaction();
- db.accountGroupMembers().insert(Collections.singleton(m), txn);
- db.accountGroupMembersAudit().insert(
- Collections.singleton(new AccountGroupMemberAudit(m,
- getAccountId())), txn);
- txn.commit();
- accountCache.evict(m.getAccountId());
- }
-
- return groupDetailFactory.create(groupId).call();
- }
- });
- }
-
- public void deleteGroupMembers(final AccountGroup.Id groupId,
- final Set<AccountGroupMember.Key> keys,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(final ReviewDb db) throws OrmException,
- NoSuchGroupException, Failure {
- final GroupControl control = groupControlFactory.validateFor(groupId);
- if (control.getAccountGroup().getType() != AccountGroup.Type.INTERNAL) {
- throw new Failure(new NameAlreadyUsedException());
- }
-
- for (final AccountGroupMember.Key k : keys) {
- if (!groupId.equals(k.getAccountGroupId())) {
- throw new Failure(new NoSuchEntityException());
- }
- }
-
- final Account.Id me = getAccountId();
- for (final AccountGroupMember.Key k : keys) {
- final AccountGroupMember m = db.accountGroupMembers().get(k);
- if (m != null) {
- if (!control.canRemove(m.getAccountId())) {
- throw new Failure(new NoSuchEntityException());
- }
-
- AccountGroupMemberAudit audit = null;
- for (AccountGroupMemberAudit a : db.accountGroupMembersAudit()
- .byGroupAccount(m.getAccountGroupId(), m.getAccountId())) {
- if (a.isActive()) {
- audit = a;
- break;
- }
- }
-
- final Transaction txn = db.beginTransaction();
- db.accountGroupMembers().delete(Collections.singleton(m), txn);
- if (audit != null) {
- audit.removed(me);
- db.accountGroupMembersAudit().update(
- Collections.singleton(audit), txn);
- } else {
- audit = new AccountGroupMemberAudit(m, me);
- audit.removedLegacy();
- db.accountGroupMembersAudit().insert(
- Collections.singleton(audit), txn);
- }
- txn.commit();
- accountCache.evict(m.getAccountId());
- }
- }
- return VoidResult.INSTANCE;
- }
- });
- }
-
- private void assertAmGroupOwner(final ReviewDb db, final AccountGroup group)
- throws Failure {
- try {
- if (!groupControlFactory.controlFor(group.getId()).isOwner()) {
- throw new Failure(new NoSuchGroupException(group.getId()));
- }
- } catch (NoSuchGroupException e) {
- throw new Failure(new NoSuchGroupException(group.getId()));
- }
- }
-
- private Account findAccount(final String nameOrEmail) throws OrmException,
- Failure {
- final Account r = accountResolver.find(nameOrEmail);
- if (r == null) {
- throw new Failure(new NoSuchAccountException(nameOrEmail));
- }
- return r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java
deleted file mode 100644
index 6c4e95ae56..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/GroupDetailFactory.java
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.admin.GroupDetail;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupMember;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.account.NoSuchGroupException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-class GroupDetailFactory extends Handler<GroupDetail> {
- interface Factory {
- GroupDetailFactory create(AccountGroup.Id groupId);
- }
-
- private final ReviewDb db;
- private final GroupControl.Factory groupControl;
- private final GroupCache groupCache;
- private final AccountInfoCacheFactory aic;
-
- private final AccountGroup.Id groupId;
- private GroupControl control;
-
- @Inject
- GroupDetailFactory(final ReviewDb db,
- final GroupControl.Factory groupControl, final GroupCache groupCache,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- @Assisted final AccountGroup.Id groupId) {
- this.db = db;
- this.groupControl = groupControl;
- this.groupCache = groupCache;
- this.aic = accountInfoCacheFactory.create();
-
- this.groupId = groupId;
- }
-
- @Override
- public GroupDetail call() throws OrmException, NoSuchGroupException {
- control = groupControl.validateFor(groupId);
- final AccountGroup group = control.getAccountGroup();
- final GroupDetail detail = new GroupDetail();
- detail.setGroup(group);
- detail.setOwnerGroup(groupCache.get(group.getOwnerGroupId()));
- switch (group.getType()) {
- case INTERNAL:
- detail.setMembers(loadMembers());
- break;
- }
- detail.setAccounts(aic.create());
- return detail;
- }
-
- private List<AccountGroupMember> loadMembers() throws OrmException {
- List<AccountGroupMember> members = new ArrayList<AccountGroupMember>();
- for (final AccountGroupMember m : db.accountGroupMembers().byGroup(groupId)) {
- if (control.canSee(m.getAccountId())) {
- aic.want(m.getAccountId());
- members.add(m);
- }
- }
-
- Collections.sort(members, new Comparator<AccountGroupMember>() {
- public int compare(final AccountGroupMember o1,
- final AccountGroupMember o2) {
- final Account a = aic.get(o1.getAccountId());
- final Account b = aic.get(o2.getAccountId());
- return n(a).compareTo(n(b));
- }
-
- private String n(final Account a) {
- String n = a.getFullName();
- if (n != null && n.length() > 0) {
- return n;
- }
-
- n = a.getPreferredEmail();
- if (n != null && n.length() > 0) {
- return n;
- }
-
- return a.getId().toString();
- }
- });
- return members;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/account/MyGroupsFactory.java b/src/main/java/com/google/gerrit/server/rpc/account/MyGroupsFactory.java
deleted file mode 100644
index 68574bc458..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/account/MyGroupsFactory.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.account;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-
-class MyGroupsFactory extends Handler<List<AccountGroup>> {
- interface Factory {
- MyGroupsFactory create();
- }
-
- private final GroupCache groupCache;
- private final IdentifiedUser user;
-
- @Inject
- MyGroupsFactory(final GroupCache groupCache, final IdentifiedUser user) {
- this.groupCache = groupCache;
- this.user = user;
- }
-
- @Override
- public List<AccountGroup> call() throws Exception {
- final Set<AccountGroup.Id> effective = user.getEffectiveGroups();
- final int cnt = effective.size();
- final List<AccountGroup> groupList = new ArrayList<AccountGroup>(cnt);
- for (final AccountGroup.Id groupId : effective) {
- groupList.add(groupCache.get(groupId));
- }
- Collections.sort(groupList, new Comparator<AccountGroup>() {
- @Override
- public int compare(AccountGroup a, AccountGroup b) {
- return a.getName().compareTo(b.getName());
- }
- });
- return groupList;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/AbandonChange.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/AbandonChange.java
deleted file mode 100644
index b72bdcc7dc..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/AbandonChange.java
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.mail.AbandonedSender;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.OrmRunnable;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-import java.util.List;
-
-class AbandonChange extends Handler<ChangeDetail> {
- interface Factory {
- AbandonChange create(PatchSet.Id patchSetId, String message);
- }
-
- private final ChangeControl.Factory changeControlFactory;
- private final ReviewDb db;
- private final IdentifiedUser currentUser;
- private final AbandonedSender.Factory abandonedSenderFactory;
- private final ChangeDetailFactory.Factory changeDetailFactory;
-
- private final PatchSet.Id patchSetId;
- @Nullable
- private final String message;
-
- @Inject
- AbandonChange(final ChangeControl.Factory changeControlFactory,
- final ReviewDb db, final IdentifiedUser currentUser,
- final AbandonedSender.Factory abandonedSenderFactory,
- final ChangeDetailFactory.Factory changeDetailFactory,
- @Assisted final PatchSet.Id patchSetId,
- @Assisted @Nullable final String message) {
- this.changeControlFactory = changeControlFactory;
- this.db = db;
- this.currentUser = currentUser;
- this.abandonedSenderFactory = abandonedSenderFactory;
- this.changeDetailFactory = changeDetailFactory;
-
- this.patchSetId = patchSetId;
- this.message = message;
- }
-
- @Override
- public ChangeDetail call() throws NoSuchChangeException, OrmException,
- EmailException, NoSuchEntityException, PatchSetInfoNotAvailableException {
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl control = changeControlFactory.validateFor(changeId);
- if (!control.canAbandon()) {
- throw new NoSuchChangeException(changeId);
- }
- final Change change = control.getChange();
- final PatchSet patch = db.patchSets().get(patchSetId);
- if (patch == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- final ChangeMessage cmsg =
- new ChangeMessage(new ChangeMessage.Key(changeId, ChangeUtil
- .messageUUID(db)), currentUser.getAccountId());
- final StringBuilder msgBuf =
- new StringBuilder("Patch Set " + change.currentPatchSetId().get()
- + ": Abandoned");
- if (message != null && message.length() > 0) {
- msgBuf.append("\n\n");
- msgBuf.append(message);
- }
- cmsg.setMessage(msgBuf.toString());
-
- Boolean dbSuccess = db.run(new OrmRunnable<Boolean, ReviewDb>() {
- public Boolean run(ReviewDb db, Transaction txn, boolean retry)
- throws OrmException {
- return doAbandonChange(message, change, patchSetId, cmsg, db, txn);
- }
- });
-
- if (dbSuccess) {
- // Email the reviewers
- final AbandonedSender cm = abandonedSenderFactory.create(change);
- cm.setFrom(currentUser.getAccountId());
- cm.setReviewDb(db);
- cm.setChangeMessage(cmsg);
- cm.send();
- }
-
- return changeDetailFactory.create(changeId).call();
- }
-
- private Boolean doAbandonChange(final String message, final Change change,
- final PatchSet.Id psid, final ChangeMessage cm, final ReviewDb db,
- final Transaction txn) throws OrmException {
-
- // Check to make sure the change status and current patchset ID haven't
- // changed while the user was typing an abandon message
- if (change.getStatus().isOpen() && change.currentPatchSetId().equals(psid)) {
- change.setStatus(Change.Status.ABANDONED);
- ChangeUtil.updated(change);
-
- final List<PatchSetApproval> approvals =
- db.patchSetApprovals().byChange(change.getId()).toList();
- for (PatchSetApproval a : approvals) {
- a.cache(change);
- }
- db.patchSetApprovals().update(approvals, txn);
-
- db.changeMessages().insert(Collections.singleton(cm), txn);
- db.changes().update(Collections.singleton(change), txn);
- return Boolean.TRUE;
- }
-
- return Boolean.FALSE;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailFactory.java
deleted file mode 100644
index 4a45e5f83f..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailFactory.java
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.data.ApprovalDetail;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.data.ChangeInfo;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetAncestor;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gerrit.server.workflow.CategoryFunction;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Creates a {@link ChangeDetail} from a {@link Change}. */
-public class ChangeDetailFactory extends Handler<ChangeDetail> {
- public interface Factory {
- ChangeDetailFactory create(Change.Id id);
- }
-
- private final ApprovalTypes approvalTypes;
- private final ChangeControl.Factory changeControlFactory;
- private final FunctionState.Factory functionState;
- private final PatchSetDetailFactory.Factory patchSetDetail;
- private final AccountInfoCacheFactory aic;
- private final ReviewDb db;
-
- private final Change.Id changeId;
-
- private ChangeDetail detail;
- private ChangeControl control;
-
- @Inject
- ChangeDetailFactory(final ApprovalTypes approvalTypes,
- final FunctionState.Factory functionState,
- final PatchSetDetailFactory.Factory patchSetDetail, final ReviewDb db,
- final ChangeControl.Factory changeControlFactory,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- @Assisted final Change.Id id) {
- this.approvalTypes = approvalTypes;
- this.functionState = functionState;
- this.patchSetDetail = patchSetDetail;
- this.db = db;
- this.changeControlFactory = changeControlFactory;
- this.aic = accountInfoCacheFactory.create();
-
- this.changeId = id;
- }
-
- @Override
- public ChangeDetail call() throws OrmException, NoSuchEntityException,
- PatchSetInfoNotAvailableException, NoSuchChangeException {
- control = changeControlFactory.validateFor(changeId);
- final Change change = control.getChange();
- final Project proj = control.getProject();
- final PatchSet patch = db.patchSets().get(change.currentPatchSetId());
- if (patch == null) {
- throw new NoSuchEntityException();
- }
-
- aic.want(change.getOwner());
-
- detail = new ChangeDetail();
- detail.setChange(change);
- detail.setAllowsAnonymous(control.forAnonymousUser().isVisible());
- detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
- detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
- changeId));
- loadPatchSets();
- loadMessages();
- if (change.currentPatchSetId() != null) {
- loadCurrentPatchSet();
- }
- load();
- detail.setAccounts(aic.create());
- return detail;
- }
-
- private void loadPatchSets() throws OrmException {
- detail.setPatchSets(db.patchSets().byChange(changeId).toList());
- }
-
- private void loadMessages() throws OrmException {
- detail.setMessages(db.changeMessages().byChange(changeId).toList());
- for (final ChangeMessage m : detail.getMessages()) {
- aic.want(m.getAuthor());
- }
- }
-
- private void load() throws OrmException {
- final PatchSet.Id psId = detail.getChange().currentPatchSetId();
- final List<PatchSetApproval> allApprovals =
- db.patchSetApprovals().byChange(changeId).toList();
-
- if (detail.getChange().getStatus().isOpen()) {
- final FunctionState fs =
- functionState.create(detail.getChange(), psId, allApprovals);
-
- final Set<ApprovalCategory.Id> missingApprovals =
- new HashSet<ApprovalCategory.Id>();
-
- final Set<ApprovalCategory.Id> currentActions =
- new HashSet<ApprovalCategory.Id>();
-
- for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
- CategoryFunction.forCategory(at.getCategory()).run(at, fs);
- if (!fs.isValid(at)) {
- missingApprovals.add(at.getCategory().getId());
- }
- }
- for (final ApprovalType at : approvalTypes.getActionTypes()) {
- if (CategoryFunction.forCategory(at.getCategory()).isValid(
- control.getCurrentUser(), at, fs)) {
- currentActions.add(at.getCategory().getId());
- }
- }
- detail.setMissingApprovals(missingApprovals);
- detail.setCurrentActions(currentActions);
- }
-
- final HashMap<Account.Id, ApprovalDetail> ad =
- new HashMap<Account.Id, ApprovalDetail>();
- for (PatchSetApproval ca : allApprovals) {
- ApprovalDetail d = ad.get(ca.getAccountId());
- if (d == null) {
- d = new ApprovalDetail(ca.getAccountId());
- ad.put(d.getAccount(), d);
- }
- if (ca.getPatchSetId().equals(psId)) {
- d.add(ca);
- }
- }
-
- final Account.Id owner = detail.getChange().getOwner();
- if (ad.containsKey(owner)) {
- // Ensure the owner always sorts to the top of the table
- //
- ad.get(owner).sortFirst();
- }
-
- aic.want(ad.keySet());
- detail.setApprovals(ad.values());
- }
-
- private void loadCurrentPatchSet() throws OrmException,
- NoSuchEntityException, PatchSetInfoNotAvailableException,
- NoSuchChangeException {
- final PatchSet.Id psId = detail.getChange().currentPatchSetId();
- final PatchSetDetailFactory loader = patchSetDetail.create(psId);
- loader.patchSet = detail.getCurrentPatchSet();
- loader.control = control;
- detail.setCurrentPatchSetDetail(loader.call());
-
- final HashSet<Change.Id> changesToGet = new HashSet<Change.Id>();
- final List<Change.Id> ancestorOrder = new ArrayList<Change.Id>();
- for (PatchSetAncestor a : db.patchSetAncestors().ancestorsOf(psId)) {
- for (PatchSet p : db.patchSets().byRevision(a.getAncestorRevision())) {
- final Change.Id ck = p.getId().getParentKey();
- if (changesToGet.add(ck)) {
- ancestorOrder.add(ck);
- }
- }
- }
-
- final RevId cprev = loader.patchSet.getRevision();
- final List<PatchSetAncestor> descendants =
- cprev != null ? db.patchSetAncestors().descendantsOf(cprev).toList()
- : Collections.<PatchSetAncestor> emptyList();
- for (final PatchSetAncestor a : descendants) {
- changesToGet.add(a.getPatchSet().getParentKey());
- }
- final Map<Change.Id, Change> m =
- db.changes().toMap(db.changes().get(changesToGet));
-
- final ArrayList<ChangeInfo> dependsOn = new ArrayList<ChangeInfo>();
- for (final Change.Id a : ancestorOrder) {
- final Change ac = m.get(a);
- if (ac != null) {
- aic.want(ac.getOwner());
- dependsOn.add(new ChangeInfo(ac));
- }
- }
-
- final ArrayList<ChangeInfo> neededBy = new ArrayList<ChangeInfo>();
- for (final PatchSetAncestor a : descendants) {
- final Change ac = m.get(a.getPatchSet().getParentKey());
- if (ac != null) {
- aic.want(ac.getOwner());
- neededBy.add(new ChangeInfo(ac));
- }
- }
-
- Collections.sort(neededBy, new Comparator<ChangeInfo>() {
- public int compare(final ChangeInfo o1, final ChangeInfo o2) {
- return o1.getId().get() - o2.getId().get();
- }
- });
-
- detail.setDependsOn(dependsOn);
- detail.setNeededBy(neededBy);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailServiceImpl.java
deleted file mode 100644
index 9acc0d1543..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeDetailServiceImpl.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.changes.ChangeDetailService;
-import com.google.gerrit.client.changes.PatchSetPublishDetail;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.data.PatchSetDetail;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.inject.Inject;
-
-class ChangeDetailServiceImpl implements ChangeDetailService {
- private final ChangeDetailFactory.Factory changeDetail;
- private final PatchSetDetailFactory.Factory patchSetDetail;
- private final PatchSetPublishDetailFactory.Factory patchSetPublishDetail;
-
- @Inject
- ChangeDetailServiceImpl(final ChangeDetailFactory.Factory changeDetail,
- final PatchSetDetailFactory.Factory patchSetDetail,
- final PatchSetPublishDetailFactory.Factory patchSetPublishDetail) {
- this.changeDetail = changeDetail;
- this.patchSetDetail = patchSetDetail;
- this.patchSetPublishDetail = patchSetPublishDetail;
- }
-
- public void changeDetail(final Change.Id id,
- final AsyncCallback<ChangeDetail> callback) {
- changeDetail.create(id).to(callback);
- }
-
- public void patchSetDetail(final PatchSet.Id id,
- final AsyncCallback<PatchSetDetail> callback) {
- patchSetDetail.create(id).to(callback);
- }
-
- public void patchSetPublishDetail(final PatchSet.Id id,
- final AsyncCallback<PatchSetPublishDetail> callback) {
- patchSetPublishDetail.create(id).to(callback);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeManageServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeManageServiceImpl.java
deleted file mode 100644
index fe6e0f36ce..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeManageServiceImpl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.changes.ChangeManageService;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.inject.Inject;
-
-class ChangeManageServiceImpl implements ChangeManageService {
- private final SubmitAction.Factory submitAction;
- private final AbandonChange.Factory abandonChangeFactory;
-
- @Inject
- ChangeManageServiceImpl(final SubmitAction.Factory patchSetAction,
- final AbandonChange.Factory abandonChangeFactory) {
- this.submitAction = patchSetAction;
- this.abandonChangeFactory = abandonChangeFactory;
- }
-
- public void submit(final PatchSet.Id patchSetId,
- final AsyncCallback<ChangeDetail> cb) {
- submitAction.create(patchSetId).to(cb);
- }
-
- public void abandonChange(final PatchSet.Id patchSetId, final String message,
- final AsyncCallback<ChangeDetail> callback) {
- abandonChangeFactory.create(patchSetId, message).to(callback);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeModule.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeModule.java
deleted file mode 100644
index ad44933eb8..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/ChangeModule.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-
-public class ChangeModule extends RpcServletModule {
- public ChangeModule() {
- super(UiRpcModule.PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- install(new FactoryModule() {
- @Override
- protected void configure() {
- factory(AbandonChange.Factory.class);
- factory(ChangeDetailFactory.Factory.class);
- factory(PatchSetDetailFactory.Factory.class);
- factory(PatchSetPublishDetailFactory.Factory.class);
- factory(SubmitAction.Factory.class);
- }
- });
- rpc(ChangeDetailServiceImpl.class);
- rpc(ChangeManageServiceImpl.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetDetailFactory.java
deleted file mode 100644
index ea96a130d5..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetDetailFactory.java
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.data.PatchSetDetail;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountPatchReview;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/** Creates a {@link PatchSetDetail} from a {@link PatchSet}. */
-class PatchSetDetailFactory extends Handler<PatchSetDetail> {
- interface Factory {
- PatchSetDetailFactory create(PatchSet.Id id);
- }
-
- private final PatchSetInfoFactory infoFactory;
- private final ReviewDb db;
- private final PatchListCache patchListCache;
- private final ChangeControl.Factory changeControlFactory;
-
- private final PatchSet.Id psId;
-
- private PatchSetDetail detail;
- ChangeControl control;
- PatchSet patchSet;
-
- @Inject
- PatchSetDetailFactory(final PatchSetInfoFactory psif, final ReviewDb db,
- final PatchListCache patchListCache,
- final ChangeControl.Factory changeControlFactory,
- @Assisted final PatchSet.Id id) {
- this.infoFactory = psif;
- this.db = db;
- this.patchListCache = patchListCache;
- this.changeControlFactory = changeControlFactory;
-
- this.psId = id;
- }
-
- @Override
- public PatchSetDetail call() throws OrmException, NoSuchEntityException,
- PatchSetInfoNotAvailableException, NoSuchChangeException {
- if (control == null || patchSet == null) {
- control = changeControlFactory.validateFor(psId.getParentKey());
- patchSet = db.patchSets().get(psId);
- if (patchSet == null) {
- throw new NoSuchEntityException();
- }
- }
-
- final PatchList list = patchListCache.get(control.getChange(), patchSet);
- final List<Patch> patches = list.toPatchList(patchSet.getId());
- final Map<Patch.Key, Patch> byKey = new HashMap<Patch.Key, Patch>();
- for (final Patch p : patches) {
- byKey.put(p.getKey(), p);
- }
-
- for (final PatchLineComment c : db.patchComments().published(psId)) {
- final Patch p = byKey.get(c.getKey().getParentKey());
- if (p != null) {
- p.setCommentCount(p.getCommentCount() + 1);
- }
- }
-
- detail = new PatchSetDetail();
- detail.setPatchSet(patchSet);
-
- detail.setInfo(infoFactory.get(psId));
- detail.setPatches(patches);
-
- final CurrentUser user = control.getCurrentUser();
- if (user instanceof IdentifiedUser) {
- // If we are signed in, compute the number of draft comments by the
- // current user on each of these patch files. This way they can more
- // quickly locate where they have pending drafts, and review them.
- //
- final Account.Id me = ((IdentifiedUser) user).getAccountId();
- for (final PatchLineComment c : db.patchComments().draft(psId, me)) {
- final Patch p = byKey.get(c.getKey().getParentKey());
- if (p != null) {
- p.setDraftCount(p.getDraftCount() + 1);
- }
- }
-
- for (AccountPatchReview r : db.accountPatchReviews().byReviewer(me, psId)) {
- final Patch p = byKey.get(r.getKey().getPatchKey());
- if (p != null) {
- p.setReviewedByCurrentUser(true);
- }
- }
- }
-
- return detail;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetPublishDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetPublishDetailFactory.java
deleted file mode 100644
index d5e16a6f88..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/PatchSetPublishDetailFactory.java
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import com.google.gerrit.client.changes.PatchSetPublishDetail;
-import com.google.gerrit.client.data.AccountInfoCache;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> {
- interface Factory {
- PatchSetPublishDetailFactory create(PatchSet.Id patchSetId);
- }
-
- private final ProjectCache projectCache;
- private final PatchSetInfoFactory infoFactory;
- private final ApprovalTypes approvalTypes;
- private final ReviewDb db;
- private final ChangeControl.Factory changeControlFactory;
- private final AccountInfoCacheFactory aic;
- private final IdentifiedUser user;
-
- private final PatchSet.Id patchSetId;
-
- private AccountInfoCache accounts;
- private PatchSetInfo patchSetInfo;
- private Change change;
- private List<PatchLineComment> drafts;
- private Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
- private Map<ApprovalCategory.Id, PatchSetApproval> given;
-
- @Inject
- PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory,
- final ProjectCache projectCache, final ApprovalTypes approvalTypes,
- final ReviewDb db,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- final ChangeControl.Factory changeControlFactory,
- final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) {
- this.projectCache = projectCache;
- this.infoFactory = infoFactory;
- this.approvalTypes = approvalTypes;
- this.db = db;
- this.changeControlFactory = changeControlFactory;
- this.aic = accountInfoCacheFactory.create();
- this.user = user;
-
- this.patchSetId = patchSetId;
- }
-
- @Override
- public PatchSetPublishDetail call() throws OrmException,
- PatchSetInfoNotAvailableException, NoSuchChangeException {
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl control = changeControlFactory.validateFor(changeId);
- change = control.getChange();
- patchSetInfo = infoFactory.get(patchSetId);
- drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
-
- allowed = new HashMap<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>>();
- given = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- if (change.getStatus().isOpen()
- && patchSetId.equals(change.currentPatchSetId())) {
- computeAllowed();
- for (final PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(
- patchSetId, user.getAccountId())) {
- given.put(a.getCategoryId(), a);
- }
- }
-
- aic.want(change.getOwner());
- accounts = aic.create();
-
- PatchSetPublishDetail detail = new PatchSetPublishDetail();
- detail.setAccounts(accounts);
- detail.setPatchSetInfo(patchSetInfo);
- detail.setChange(change);
- detail.setDrafts(drafts);
- detail.setAllowed(allowed);
- detail.setGiven(given);
-
- return detail;
- }
-
- private void computeAllowed() {
- final Set<AccountGroup.Id> am = user.getEffectiveGroups();
- final ProjectState pe = projectCache.get(change.getProject());
- computeAllowed(am, pe.getLocalRights());
- computeAllowed(am, pe.getInheritedRights());
- }
-
- private void computeAllowed(final Set<AccountGroup.Id> am,
- final Collection<ProjectRight> list) {
- for (final ProjectRight r : list) {
- if (!am.contains(r.getAccountGroupId())) {
- continue;
- }
-
- Set<ApprovalCategoryValue.Id> s = allowed.get(r.getApprovalCategoryId());
- if (s == null) {
- s = new HashSet<ApprovalCategoryValue.Id>();
- allowed.put(r.getApprovalCategoryId(), s);
- }
-
- final ApprovalType at =
- approvalTypes.getApprovalType(r.getApprovalCategoryId());
- for (short m = r.getMinValue(); m <= r.getMaxValue(); m++) {
- final ApprovalCategoryValue v = at.getValue(m);
- if (v != null) {
- s.add(v.getId());
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/changedetail/SubmitAction.java b/src/main/java/com/google/gerrit/server/rpc/changedetail/SubmitAction.java
deleted file mode 100644
index 8f9b5c362d..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/changedetail/SubmitAction.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.changedetail;
-
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.SUBMIT;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.data.ChangeDetail;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.git.MergeQueue;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gerrit.server.workflow.CategoryFunction;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-class SubmitAction extends Handler<ChangeDetail> {
- interface Factory {
- SubmitAction create(PatchSet.Id patchSetId);
- }
-
- private final ReviewDb db;
- private final MergeQueue merger;
- private final ApprovalTypes approvalTypes;
- private final FunctionState.Factory functionState;
- private final IdentifiedUser user;
- private final ChangeDetailFactory.Factory changeDetailFactory;
-
- private final PatchSet.Id patchSetId;
-
- @Inject
- SubmitAction(final ReviewDb db, final MergeQueue mq, final ApprovalTypes at,
- final FunctionState.Factory fs, final IdentifiedUser user,
- final ChangeDetailFactory.Factory changeDetailFactory,
- @Assisted final PatchSet.Id patchSetId) {
- this.db = db;
- this.merger = mq;
- this.approvalTypes = at;
- this.functionState = fs;
- this.user = user;
- this.changeDetailFactory = changeDetailFactory;
-
- this.patchSetId = patchSetId;
- }
-
- @Override
- public ChangeDetail call() throws OrmException, NoSuchEntityException,
- IllegalStateException, PatchSetInfoNotAvailableException,
- NoSuchChangeException {
- final Change change = db.changes().get(patchSetId.getParentKey());
- if (change == null) {
- throw new NoSuchEntityException();
- }
-
- if (!patchSetId.equals(change.currentPatchSetId())) {
- throw new IllegalStateException("Patch set " + patchSetId
- + " not current");
- }
- if (change.getStatus().isClosed()) {
- throw new IllegalStateException("Change" + change.getId() + " is closed");
- }
-
- final List<PatchSetApproval> allApprovals =
- new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
- patchSetId).toList());
-
- final PatchSetApproval.Key ak =
- new PatchSetApproval.Key(patchSetId, user.getAccountId(), SUBMIT);
- PatchSetApproval myAction = null;
- boolean isnew = true;
- for (final PatchSetApproval ca : allApprovals) {
- if (ak.equals(ca.getKey())) {
- isnew = false;
- myAction = ca;
- myAction.setValue((short) 1);
- myAction.setGranted();
- break;
- }
- }
- if (myAction == null) {
- myAction = new PatchSetApproval(ak, (short) 1);
- allApprovals.add(myAction);
- }
-
- final ApprovalType actionType =
- approvalTypes.getApprovalType(myAction.getCategoryId());
- if (actionType == null || !actionType.getCategory().isAction()) {
- throw new IllegalArgumentException(actionType.getCategory().getName()
- + " not an action");
- }
-
- final FunctionState fs =
- functionState.create(change, patchSetId, allApprovals);
- for (ApprovalType c : approvalTypes.getApprovalTypes()) {
- CategoryFunction.forCategory(c.getCategory()).run(c, fs);
- }
- if (!CategoryFunction.forCategory(actionType.getCategory()).isValid(user,
- actionType, fs)) {
- throw new IllegalStateException(actionType.getCategory().getName()
- + " not permitted");
- }
- fs.normalize(actionType, myAction);
- if (myAction.getValue() <= 0) {
- throw new IllegalStateException(actionType.getCategory().getName()
- + " not permitted");
- }
-
- if (change.getStatus() == Change.Status.NEW) {
- change.setStatus(Change.Status.SUBMITTED);
- ChangeUtil.updated(change);
- }
-
- final Transaction txn = db.beginTransaction();
- db.changes().update(Collections.singleton(change), txn);
- if (change.getStatus().isClosed()) {
- db.patchSetApprovals().update(fs.getDirtyChangeApprovals(), txn);
- }
- if (isnew) {
- db.patchSetApprovals().insert(Collections.singleton(myAction), txn);
- } else {
- db.patchSetApprovals().update(Collections.singleton(myAction), txn);
- }
- txn.commit();
-
- if (change.getStatus() == Change.Status.SUBMITTED) {
- merger.merge(change.getDest());
- }
-
- return changeDetailFactory.create(change.getId()).call();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/AddReviewer.java b/src/main/java/com/google/gerrit/server/rpc/patch/AddReviewer.java
deleted file mode 100644
index f74935c907..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/AddReviewer.java
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.patches.AddReviewerResult;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.mail.AddReviewerSender;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gerrit.server.rpc.changedetail.ChangeDetailFactory;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.OrmRunnable;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-class AddReviewer extends Handler<AddReviewerResult> {
- interface Factory {
- AddReviewer create(Change.Id changeId, Collection<String> nameOrEmails);
- }
-
- private final AddReviewerSender.Factory addReviewerSenderFactory;
- private final AccountResolver accountResolver;
- private final ChangeControl.Factory changeControlFactory;
- private final ChangeDetailFactory.Factory changeDetailFactory;
- private final ReviewDb db;
- private final IdentifiedUser currentUser;
- private final IdentifiedUser.GenericFactory identifiedUserFactory;
- private final ApprovalCategory.Id addReviewerCategoryId;
-
- private final Change.Id changeId;
- private final Collection<String> reviewers;
-
- @Inject
- AddReviewer(final AddReviewerSender.Factory addReviewerSenderFactory,
- final AccountResolver accountResolver,
- final ChangeControl.Factory changeControlFactory, final ReviewDb db,
- final IdentifiedUser.GenericFactory identifiedUserFactory,
- final IdentifiedUser currentUser, final ApprovalTypes approvalTypes,
- final ChangeDetailFactory.Factory changeDetailFactory,
- @Assisted final Change.Id changeId,
- @Assisted final Collection<String> nameOrEmails) {
- this.addReviewerSenderFactory = addReviewerSenderFactory;
- this.accountResolver = accountResolver;
- this.db = db;
- this.changeControlFactory = changeControlFactory;
- this.identifiedUserFactory = identifiedUserFactory;
- this.currentUser = currentUser;
- this.changeDetailFactory = changeDetailFactory;
-
- final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
- addReviewerCategoryId =
- allTypes.get(allTypes.size() - 1).getCategory().getId();
-
- this.changeId = changeId;
- this.reviewers = nameOrEmails;
- }
-
- @Override
- public AddReviewerResult call() throws Exception {
- final Set<Account.Id> reviewerIds = new HashSet<Account.Id>();
- final ChangeControl control = changeControlFactory.validateFor(changeId);
-
- final AddReviewerResult result = new AddReviewerResult();
- for (final String nameOrEmail : reviewers) {
- final Account account = accountResolver.find(nameOrEmail);
- if (account == null) {
- result.addError(new AddReviewerResult.Error(
- AddReviewerResult.Error.Type.ACCOUNT_NOT_FOUND, nameOrEmail));
- continue;
- }
-
- final IdentifiedUser user = identifiedUserFactory.create(account.getId());
- if (!control.forUser(user).isVisible()) {
- result.addError(new AddReviewerResult.Error(
- AddReviewerResult.Error.Type.CHANGE_NOT_VISIBLE, nameOrEmail));
- continue;
- }
-
- reviewerIds.add(account.getId());
- }
-
- if (reviewerIds.isEmpty()) {
- return result;
- }
-
- // Add the reviewers to the database
- //
- final Set<Account.Id> added = new HashSet<Account.Id>();
- db.run(new OrmRunnable<Object, ReviewDb>() {
- public Object run(ReviewDb db, Transaction txn, boolean retry)
- throws OrmException {
- final PatchSet.Id psid = control.getChange().currentPatchSetId();
- for (final Account.Id reviewer : reviewerIds) {
- if (!exists(psid, reviewer)) {
- // This reviewer has not entered an approval for this change yet.
- //
- final PatchSetApproval myca = dummyApproval(psid, reviewer);
- db.patchSetApprovals().insert(Collections.singleton(myca), txn);
- added.add(reviewer);
- }
- }
- return null;
- }
- });
-
- // Email the reviewers
- //
- final AddReviewerSender cm;
- cm = addReviewerSenderFactory.create(control.getChange());
- cm.setFrom(currentUser.getAccountId());
- cm.setReviewDb(db);
- cm.addReviewers(added);
- cm.send();
-
- result.setChange(changeDetailFactory.create(changeId).call());
- return result;
- }
-
- private boolean exists(final PatchSet.Id patchSetId,
- final Account.Id reviewerId) throws OrmException {
- return db.patchSetApprovals().byPatchSetUser(patchSetId, reviewerId)
- .iterator().hasNext();
- }
-
- private PatchSetApproval dummyApproval(final PatchSet.Id patchSetId,
- final Account.Id reviewerId) {
- return new PatchSetApproval(new PatchSetApproval.Key(patchSetId,
- reviewerId, addReviewerCategoryId), (short) 0);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/CommentDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/patch/CommentDetailFactory.java
deleted file mode 100644
index 2da052d5c3..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/CommentDetailFactory.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.patches.CommentDetail;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-class CommentDetailFactory extends Handler<CommentDetail> {
- interface Factory {
- CommentDetailFactory create(Patch.Key patchKey,
- @Assisted("patchSetA") PatchSet.Id patchSetA,
- @Assisted("patchSetB") PatchSet.Id patchSetB);
- }
-
- private final ReviewDb db;
- private final ChangeControl.Factory changeControlFactory;
- private final AccountInfoCacheFactory aic;
-
- private final Patch.Key patchKey;
- private final PatchSet.Id psa;
- private final PatchSet.Id psb;
-
- private final PatchSet.Id patchSetId;
- private final Change.Id changeId;
-
- @Inject
- CommentDetailFactory(final ReviewDb db,
- final ChangeControl.Factory changeControlFactory,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- @Assisted final Patch.Key patchKey,
- @Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
- @Assisted("patchSetB") final PatchSet.Id patchSetB) {
- this.db = db;
- this.changeControlFactory = changeControlFactory;
- this.aic = accountInfoCacheFactory.create();
-
- this.patchKey = patchKey;
- this.psa = patchSetA;
- this.psb = patchSetB;
-
- patchSetId = patchKey.getParentKey();
- changeId = patchSetId.getParentKey();
- }
-
- @Override
- public CommentDetail call() throws OrmException, NoSuchChangeException {
- validatePatchSetId(psa);
- validatePatchSetId(psb);
-
- final ChangeControl control = changeControlFactory.validateFor(changeId);
- final String pn = patchKey.getFileName();
- final CommentDetail r = new CommentDetail(psa, psb);
-
- final List<Patch> historyList = new ArrayList<Patch>();
- final Map<PatchSet.Id, Patch> bySet = new HashMap<PatchSet.Id, Patch>();
- for (final PatchSet ps : db.patchSets().byChange(changeId)) {
- final Patch p = new Patch(new Patch.Key(ps.getId(), pn));
- historyList.add(p);
- bySet.put(ps.getId(), p);
- }
-
- for (PatchLineComment c : db.patchComments().published(changeId, pn)) {
- if (r.include(c)) {
- aic.want(c.getAuthor());
- }
- final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
- final Patch patch = bySet.get(psId);
- if (patch != null) {
- patch.setCommentCount(patch.getCommentCount() + 1);
- }
- }
-
- final CurrentUser user = control.getCurrentUser();
- if (user instanceof IdentifiedUser) {
- final Account.Id me = ((IdentifiedUser) user).getAccountId();
- for (PatchLineComment c : db.patchComments().draft(changeId, pn, me)) {
- if (r.include(c)) {
- aic.want(me);
- }
- final PatchSet.Id psId = c.getKey().getParentKey().getParentKey();
- final Patch patch = bySet.get(psId);
- if (patch != null) {
- patch.setDraftCount(patch.getDraftCount() + 1);
- }
- }
- }
-
- r.setHistory(historyList);
- r.setAccountInfoCache(aic.create());
- return r;
- }
-
- private void validatePatchSetId(final PatchSet.Id psId)
- throws NoSuchChangeException {
- if (psId == null) { // OK, means use base;
- } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
- } else {
- throw new NoSuchChangeException(changeId);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/PatchDetailServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/patch/PatchDetailServiceImpl.java
deleted file mode 100644
index 087a97a54a..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/PatchDetailServiceImpl.java
+++ /dev/null
@@ -1,419 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.data.ApprovalSummary;
-import com.google.gerrit.client.data.ApprovalSummarySet;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.PatchScriptSettings;
-import com.google.gerrit.client.patches.AddReviewerResult;
-import com.google.gerrit.client.patches.CommentDetail;
-import com.google.gerrit.client.patches.PatchDetailService;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountPatchReview;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.Patch.Key;
-import com.google.gerrit.client.rpc.NoSuchEntityException;
-import com.google.gerrit.server.BaseServiceImplementation;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountInfoCacheFactory;
-import com.google.gerrit.server.mail.CommentSender;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.OrmRunnable;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-class PatchDetailServiceImpl extends BaseServiceImplementation implements
- PatchDetailService {
- private final Logger log = LoggerFactory.getLogger(getClass());
- private final CommentSender.Factory commentSenderFactory;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final ApprovalTypes approvalTypes;
-
- private final AccountInfoCacheFactory.Factory accountInfoCacheFactory;
- private final AddReviewer.Factory addReviewerFactory;
- private final ChangeControl.Factory changeControlFactory;
- private final CommentDetailFactory.Factory commentDetailFactory;
- private final FunctionState.Factory functionStateFactory;
- private final PatchScriptFactory.Factory patchScriptFactoryFactory;
- private final SaveDraft.Factory saveDraftFactory;
-
- @Inject
- PatchDetailServiceImpl(final Provider<ReviewDb> schema,
- final Provider<CurrentUser> currentUser,
- final CommentSender.Factory commentSenderFactory,
- final PatchSetInfoFactory patchSetInfoFactory,
- final ApprovalTypes approvalTypes,
- final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
- final AddReviewer.Factory addReviewerFactory,
- final ChangeControl.Factory changeControlFactory,
- final CommentDetailFactory.Factory commentDetailFactory,
- final FunctionState.Factory functionStateFactory,
- final PatchScriptFactory.Factory patchScriptFactoryFactory,
- final SaveDraft.Factory saveDraftFactory) {
- super(schema, currentUser);
- this.patchSetInfoFactory = patchSetInfoFactory;
- this.commentSenderFactory = commentSenderFactory;
- this.approvalTypes = approvalTypes;
-
- this.accountInfoCacheFactory = accountInfoCacheFactory;
- this.addReviewerFactory = addReviewerFactory;
- this.changeControlFactory = changeControlFactory;
- this.commentDetailFactory = commentDetailFactory;
- this.functionStateFactory = functionStateFactory;
- this.patchScriptFactoryFactory = patchScriptFactoryFactory;
- this.saveDraftFactory = saveDraftFactory;
- }
-
- public void patchScript(final Patch.Key patchKey, final PatchSet.Id psa,
- final PatchSet.Id psb, final PatchScriptSettings s,
- final AsyncCallback<PatchScript> callback) {
- if (psb == null) {
- callback.onFailure(new NoSuchEntityException());
- return;
- }
- patchScriptFactoryFactory.create(patchKey, psa, psb, s).to(callback);
- }
-
- public void patchComments(final Patch.Key patchKey, final PatchSet.Id psa,
- final PatchSet.Id psb, final AsyncCallback<CommentDetail> callback) {
- if (psb == null) {
- callback.onFailure(new NoSuchEntityException());
- return;
- }
- commentDetailFactory.create(patchKey, psa, psb).to(callback);
- }
-
- public void saveDraft(final PatchLineComment comment,
- final AsyncCallback<PatchLineComment> callback) {
- saveDraftFactory.create(comment).to(callback);
- }
-
- public void deleteDraft(final PatchLineComment.Key commentKey,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(ReviewDb db) throws OrmException, Failure {
- final PatchLineComment comment = db.patchComments().get(commentKey);
- if (comment == null) {
- throw new Failure(new NoSuchEntityException());
- }
- if (!getAccountId().equals(comment.getAuthor())) {
- throw new Failure(new NoSuchEntityException());
- }
- if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
- throw new Failure(new IllegalStateException("Comment published"));
- }
- db.patchComments().delete(Collections.singleton(comment));
- return VoidResult.INSTANCE;
- }
- });
- }
-
- public void publishComments(final PatchSet.Id psid, final String message,
- final Set<ApprovalCategoryValue.Id> approvals,
- final AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(ReviewDb db) throws OrmException, Failure {
- final PublishResult r;
-
- r = db.run(new OrmRunnable<PublishResult, ReviewDb>() {
- public PublishResult run(ReviewDb db, Transaction txn, boolean retry)
- throws OrmException {
- return doPublishComments(psid, message, approvals, db, txn);
- }
- });
-
- try {
- final CommentSender cm;
- cm = commentSenderFactory.create(r.change);
- cm.setFrom(getAccountId());
- cm.setPatchSet(r.patchSet, patchSetInfoFactory.get(psid));
- cm.setChangeMessage(r.message);
- cm.setPatchLineComments(r.comments);
- cm.setReviewDb(db);
- cm.send();
- } catch (EmailException e) {
- log.error("Cannot send comments by email for patch set " + psid, e);
- throw new Failure(e);
- } catch (PatchSetInfoNotAvailableException e) {
- log.error("Failed to obtain PatchSetInfo for patch set " + psid, e);
- throw new Failure(e);
- }
- return VoidResult.INSTANCE;
- }
- });
- }
-
- /**
- * Update the reviewed status for the file by user @code{account}
- */
- public void setReviewedByCurrentUser(final Key patchKey,
- final boolean reviewed, AsyncCallback<VoidResult> callback) {
- run(callback, new Action<VoidResult>() {
- public VoidResult run(ReviewDb db) throws OrmException {
- Account.Id account = getAccountId();
- AccountPatchReview.Key key =
- new AccountPatchReview.Key(patchKey, account);
- AccountPatchReview apr = db.accountPatchReviews().get(key);
- if (apr == null && reviewed) {
- db.accountPatchReviews().insert(
- Collections.singleton(new AccountPatchReview(patchKey, account)));
- } else if (apr != null && !reviewed) {
- db.accountPatchReviews().delete(Collections.singleton(apr));
- }
- return VoidResult.INSTANCE;
- }
- });
- }
-
-
- private static class PublishResult {
- Change change;
- PatchSet patchSet;
- ChangeMessage message;
- List<PatchLineComment> comments;
- }
-
- private PublishResult doPublishComments(final PatchSet.Id psid,
- final String messageText, final Set<ApprovalCategoryValue.Id> approvals,
- final ReviewDb db, final Transaction txn) throws OrmException {
- final PublishResult r = new PublishResult();
- final Account.Id me = getAccountId();
- r.change = db.changes().get(psid.getParentKey());
- r.patchSet = db.patchSets().get(psid);
- if (r.change == null || r.patchSet == null) {
- throw new OrmException(new NoSuchEntityException());
- }
-
- final boolean iscurrent = psid.equals(r.change.currentPatchSetId());
- r.comments = db.patchComments().draft(psid, me).toList();
- for (final PatchLineComment c : r.comments) {
- c.setStatus(PatchLineComment.Status.PUBLISHED);
- c.updated();
- }
- db.patchComments().update(r.comments, txn);
-
- final StringBuilder msgbuf = new StringBuilder();
- final Map<ApprovalCategory.Id, ApprovalCategoryValue.Id> values =
- new HashMap<ApprovalCategory.Id, ApprovalCategoryValue.Id>();
- for (final ApprovalCategoryValue.Id v : approvals) {
- values.put(v.getParentKey(), v);
- }
-
- final boolean applyApprovals = iscurrent && r.change.getStatus().isOpen();
- final Map<ApprovalCategory.Id, PatchSetApproval> have =
- new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- for (PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(psid, me)) {
- have.put(a.getCategoryId(), a);
- }
- for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
- final ApprovalCategoryValue.Id v = values.get(at.getCategory().getId());
- if (v == null) {
- continue;
- }
-
- final ApprovalCategoryValue val = at.getValue(v.get());
- if (val == null) {
- continue;
- }
-
- PatchSetApproval mycatpp = have.remove(v.getParentKey());
- if (mycatpp == null) {
- if (msgbuf.length() > 0) {
- msgbuf.append("; ");
- }
- msgbuf.append(val.getName());
- if (applyApprovals) {
- mycatpp =
- new PatchSetApproval(new PatchSetApproval.Key(psid, me, v
- .getParentKey()), v.get());
- db.patchSetApprovals().insert(Collections.singleton(mycatpp), txn);
- }
-
- } else if (mycatpp.getValue() != v.get()) {
- if (msgbuf.length() > 0) {
- msgbuf.append("; ");
- }
- msgbuf.append(val.getName());
- if (applyApprovals) {
- mycatpp.setValue(v.get());
- mycatpp.setGranted();
- db.patchSetApprovals().update(Collections.singleton(mycatpp), txn);
- }
- }
- }
- if (applyApprovals) {
- db.patchSetApprovals().delete(have.values(), txn);
- }
-
- if (msgbuf.length() > 0) {
- msgbuf.insert(0, "Patch Set " + psid.get() + ": ");
- msgbuf.append("\n\n");
- } else if (!iscurrent) {
- msgbuf.append("Patch Set " + psid.get() + ":\n\n");
- }
- if (messageText != null) {
- msgbuf.append(messageText);
- }
- if (msgbuf.length() > 0) {
- r.message =
- new ChangeMessage(new ChangeMessage.Key(r.change.getId(), ChangeUtil
- .messageUUID(db)), me);
- r.message.setMessage(msgbuf.toString());
- db.changeMessages().insert(Collections.singleton(r.message), txn);
- }
-
- ChangeUtil.updated(r.change);
- db.changes().update(Collections.singleton(r.change), txn);
- return r;
- }
-
- public void addReviewers(final Change.Id id, final List<String> reviewers,
- final AsyncCallback<AddReviewerResult> callback) {
- addReviewerFactory.create(id, reviewers).to(callback);
- }
-
- public void userApprovals(final Set<Change.Id> cids, final Account.Id aid,
- final AsyncCallback<ApprovalSummarySet> callback) {
- run(callback, new Action<ApprovalSummarySet>() {
- public ApprovalSummarySet run(ReviewDb db)
- throws OrmException {
- final Map<Change.Id, ApprovalSummary> approvals =
- new HashMap<Change.Id, ApprovalSummary>();
- final AccountInfoCacheFactory aicFactory =
- accountInfoCacheFactory.create();
-
- aicFactory.want(aid);
- for (final Change.Id id : cids) {
- try {
- final ChangeControl cc = changeControlFactory.validateFor(id);
- final Change change = cc.getChange();
- final PatchSet.Id ps_id = change.currentPatchSetId();
- final Map<ApprovalCategory.Id, PatchSetApproval> psas =
- new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- final FunctionState fs =
- functionStateFactory.create(change, ps_id, psas.values());
-
- for (final PatchSetApproval ca : db.patchSetApprovals()
- .byPatchSetUser(ps_id, aid)) {
- final ApprovalCategory.Id category = ca.getCategoryId();
- if (change.getStatus().isOpen()) {
- fs.normalize(approvalTypes.getApprovalType(category), ca);
- }
- if (ca.getValue() == 0
- || ApprovalCategory.SUBMIT.equals(category)) {
- continue;
- }
- psas.put(category, ca);
- }
-
- approvals.put(id, new ApprovalSummary(psas.values()));
- } catch (NoSuchChangeException nsce) {
- /* The user has no access to see this change, so we
- * simply do not provide any details about it.
- */
- }
- }
- return new ApprovalSummarySet(aicFactory.create(), approvals);
- }
- });
- }
-
- public void strongestApprovals(final Set<Change.Id> cids,
- final AsyncCallback<ApprovalSummarySet> callback) {
- run(callback, new Action<ApprovalSummarySet>() {
- public ApprovalSummarySet run(ReviewDb db)
- throws OrmException {
- final Map<Change.Id, ApprovalSummary> approvals =
- new HashMap<Change.Id, ApprovalSummary>();
- final AccountInfoCacheFactory aicFactory =
- accountInfoCacheFactory.create();
-
- for (final Change.Id id : cids) {
- try {
- final ChangeControl cc = changeControlFactory.validateFor(id);
- final Change change = cc.getChange();
- final PatchSet.Id ps_id = change.currentPatchSetId();
- final Map<ApprovalCategory.Id, PatchSetApproval> psas =
- new HashMap<ApprovalCategory.Id, PatchSetApproval>();
- final FunctionState fs =
- functionStateFactory.create(change, ps_id, psas.values());
-
- for (PatchSetApproval ca : db.patchSetApprovals()
- .byPatchSet(ps_id)) {
- final ApprovalCategory.Id category = ca.getCategoryId();
- if (change.getStatus().isOpen()) {
- fs.normalize(approvalTypes.getApprovalType(category), ca);
- }
- if (ca.getValue() == 0
- || ApprovalCategory.SUBMIT.equals(category)) {
- continue;
- }
- boolean keep = true;
- if (psas.containsKey(category)) {
- final short oldValue = psas.get(category).getValue();
- final short newValue = ca.getValue();
- keep = (Math.abs(oldValue) < Math.abs(newValue))
- || ((Math.abs(oldValue) == Math.abs(newValue)
- && (newValue < oldValue)));
- }
- if (keep) {
- aicFactory.want(ca.getAccountId());
- psas.put(category, ca);
- }
- }
-
- approvals.put(id, new ApprovalSummary(psas.values()));
- } catch (NoSuchChangeException nsce) {
- /* The user has no access to see this change, so we
- * simply do not provide any details about it.
- */
- }
- }
-
- return new ApprovalSummarySet(aicFactory.create(), approvals);
- }
- });
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/PatchModule.java b/src/main/java/com/google/gerrit/server/rpc/patch/PatchModule.java
deleted file mode 100644
index 159ac20987..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/PatchModule.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-
-public class PatchModule extends RpcServletModule {
- public PatchModule() {
- super(UiRpcModule.PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- install(new FactoryModule() {
- @Override
- protected void configure() {
- factory(AddReviewer.Factory.class);
- factory(CommentDetailFactory.Factory.class);
- factory(PatchScriptFactory.Factory.class);
- factory(SaveDraft.Factory.class);
- }
- });
- rpc(PatchDetailServiceImpl.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptBuilder.java b/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptBuilder.java
deleted file mode 100644
index 6ddc84648e..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptBuilder.java
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.data.EditList;
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.PatchScriptSettings;
-import com.google.gerrit.client.data.SparseFileContent;
-import com.google.gerrit.client.data.PatchScript.DisplayMethod;
-import com.google.gerrit.client.patches.CommentDetail;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.Patch.PatchType;
-import com.google.gerrit.server.FileTypeRegistry;
-import com.google.gerrit.server.patch.PatchListEntry;
-import com.google.gerrit.server.patch.Text;
-
-import eu.medsea.mimeutil.MimeType;
-import eu.medsea.mimeutil.MimeUtil2;
-
-import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectLoader;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.treewalk.TreeWalk;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-class PatchScriptBuilder {
- static final int MAX_CONTEXT = 5000000;
- static final int BIG_FILE = 9000;
-
- private static final Comparator<Edit> EDIT_SORT = new Comparator<Edit>() {
- @Override
- public int compare(final Edit o1, final Edit o2) {
- return o1.getBeginA() - o2.getBeginA();
- }
- };
-
- private final List<String> header;
- private Repository db;
- private Change change;
- private PatchScriptSettings settings;
- private ObjectId aId;
- private ObjectId bId;
-
- private final Side a;
- private final Side b;
-
- private List<Edit> edits;
- private final FileTypeRegistry registry;
-
- PatchScriptBuilder(final FileTypeRegistry ftr) {
- header = new ArrayList<String>();
- a = new Side();
- b = new Side();
- registry = ftr;
- }
-
- void setRepository(final Repository r) {
- db = r;
- }
-
- void setChange(final Change c) {
- this.change = c;
- }
-
- void setSettings(final PatchScriptSettings s) {
- settings = s;
- }
-
- void setTrees(final ObjectId a, final ObjectId b) {
- aId = a;
- bId = b;
- }
-
- private int context() {
- return settings.getContext();
- }
-
- PatchScript toPatchScript(final PatchListEntry contentWS,
- final CommentDetail comments, final PatchListEntry contentAct)
- throws IOException {
- if (contentAct.getPatchType() == PatchType.N_WAY) {
- // For a diff --cc format we don't support converting it into
- // a patch script. Instead treat everything as a file header.
- //
- return new PatchScript(change.getKey(), contentAct.getHeaderLines(),
- settings, a.dst, b.dst, Collections.<Edit> emptyList(),
- a.displayMethod, b.displayMethod);
- }
-
- a.path = oldName(contentAct);
- b.path = newName(contentAct);
-
- a.resolve(null, aId);
- b.resolve(a, bId);
-
- edits = new ArrayList<Edit>(contentAct.getEdits());
- ensureCommentsVisible(comments);
- header.addAll(contentAct.getHeaderLines());
-
- if (a.mode == FileMode.GITLINK || b.mode == FileMode.GITLINK) {
-
- } else if (a.src == b.src && a.src.size() <= context()
- && contentAct.getEdits().isEmpty()) {
- // Odd special case; the files are identical (100% rename or copy)
- // and the user has asked for context that is larger than the file.
- // Send them the entire file, with an empty edit after the last line.
- //
- for (int i = 0; i < a.src.size(); i++) {
- a.src.addLineTo(a.dst, i);
- }
- edits = new ArrayList<Edit>(1);
- edits.add(new Edit(a.src.size(), a.src.size()));
- } else {
- if (BIG_FILE < Math.max(a.src.size(), b.src.size()) && 25 < context()) {
- settings.setContext(25);
- }
- packContent();
- }
-
- if (contentWS != contentAct) {
- // The edit list we used to pack the file contents doesn't honor the
- // whitespace settings requested. Instead we must rebuild our edit
- // list around the whitespace edit list.
- //
- edits = new ArrayList<Edit>(contentWS.getEdits());
- ensureCommentsVisible(comments);
- }
-
- return new PatchScript(change.getKey(), header, settings, a.dst, b.dst,
- edits, a.displayMethod, b.displayMethod);
- }
-
- private static String oldName(final PatchListEntry entry) {
- switch (entry.getChangeType()) {
- case ADDED:
- return null;
- case DELETED:
- case MODIFIED:
- return entry.getNewName();
- case COPIED:
- case RENAMED:
- default:
- return entry.getOldName();
- }
- }
-
- private static String newName(final PatchListEntry entry) {
- switch (entry.getChangeType()) {
- case DELETED:
- return null;
- case ADDED:
- case MODIFIED:
- case COPIED:
- case RENAMED:
- default:
- return entry.getNewName();
- }
- }
-
- private void ensureCommentsVisible(final CommentDetail comments) {
- if (comments.getCommentsA().isEmpty() && comments.getCommentsB().isEmpty()) {
- // No comments, no additional dummy edits are required.
- //
- return;
- }
-
- // Construct empty Edit blocks around each location where a comment is.
- // This will force the later packContent method to include the regions
- // containing comments, potentially combining those regions together if
- // they have overlapping contexts. UI renders will also be able to make
- // correct hunks from this, but because the Edit is empty they will not
- // style it specially.
- //
- final List<Edit> empty = new ArrayList<Edit>();
- int lastLine;
-
- lastLine = -1;
- for (PatchLineComment plc : comments.getCommentsA()) {
- final int a = plc.getLine();
- if (lastLine != a) {
- final int b = mapA2B(a - 1);
- if (0 <= b) {
- safeAdd(empty, new Edit(a - 1, b));
- }
- lastLine = a;
- }
- }
-
- lastLine = -1;
- for (PatchLineComment plc : comments.getCommentsB()) {
- final int b = plc.getLine();
- if (lastLine != b) {
- final int a = mapB2A(b - 1);
- if (0 <= a) {
- safeAdd(empty, new Edit(a, b - 1));
- }
- lastLine = b;
- }
- }
-
- // Sort the final list by the index in A, so packContent can combine
- // them correctly later.
- //
- edits.addAll(empty);
- Collections.sort(edits, EDIT_SORT);
- }
-
- private void safeAdd(final List<Edit> empty, final Edit toAdd) {
- final int a = toAdd.getBeginA();
- final int b = toAdd.getBeginB();
- for (final Edit e : edits) {
- if (e.getBeginA() <= a && a <= e.getEndA()) {
- return;
- }
- if (e.getBeginB() <= b && b <= e.getEndB()) {
- return;
- }
- }
- empty.add(toAdd);
- }
-
- private int mapA2B(final int a) {
- if (edits.isEmpty()) {
- // Magic special case of an unmodified file.
- //
- return a;
- }
-
- for (int i = 0; i < edits.size(); i++) {
- final Edit e = edits.get(i);
- if (a < e.getBeginA()) {
- if (i == 0) {
- // Special case of context at start of file.
- //
- return a;
- }
- return e.getBeginB() - (e.getBeginA() - a);
- }
- if (e.getBeginA() <= a && a <= e.getEndA()) {
- return -1;
- }
- }
-
- final Edit last = edits.get(edits.size() - 1);
- return last.getBeginB() + (a - last.getEndA());
- }
-
- private int mapB2A(final int b) {
- if (edits.isEmpty()) {
- // Magic special case of an unmodified file.
- //
- return b;
- }
-
- for (int i = 0; i < edits.size(); i++) {
- final Edit e = edits.get(i);
- if (b < e.getBeginB()) {
- if (i == 0) {
- // Special case of context at start of file.
- //
- return b;
- }
- return e.getBeginA() - (e.getBeginB() - b);
- }
- if (e.getBeginB() <= b && b <= e.getEndB()) {
- return -1;
- }
- }
-
- final Edit last = edits.get(edits.size() - 1);
- return last.getBeginA() + (b - last.getEndB());
- }
-
- private void packContent() {
- EditList list = new EditList(edits, context(), a.src.size(), b.src.size());
- for (final EditList.Hunk hunk : list.getHunks()) {
- while (hunk.next()) {
- if (hunk.isContextLine()) {
- a.src.addLineTo(a.dst, hunk.getCurA());
- hunk.incBoth();
-
- } else if (hunk.isDeletedA()) {
- a.src.addLineTo(a.dst, hunk.getCurA());
- hunk.incA();
-
- } else if (hunk.isInsertedB()) {
- b.src.addLineTo(b.dst, hunk.getCurB());
- hunk.incB();
- }
- }
- }
- }
-
- private class Side {
- String path;
- ObjectId id;
- FileMode mode;
- Text src;
- MimeType mimeType = MimeUtil2.UNKNOWN_MIME_TYPE;
- DisplayMethod displayMethod = DisplayMethod.DIFF;
- final SparseFileContent dst = new SparseFileContent();
-
- void resolve(final Side other, final ObjectId within) throws IOException {
- try {
- final TreeWalk tw = find(within);
-
- id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
- mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
-
- final boolean reuse =
- other != null && other.id.equals(id) && other.mode == mode;
-
- if (reuse) {
- src = other.src;
-
- } else if (mode.getObjectType() == Constants.OBJ_BLOB) {
- final ObjectLoader ldr = db.openObject(id);
- if (ldr == null) {
- throw new MissingObjectException(id, Constants.TYPE_BLOB);
- }
- final byte[] data = ldr.getCachedBytes();
- if (ldr.getType() != Constants.OBJ_BLOB) {
- throw new IncorrectObjectTypeException(id, Constants.TYPE_BLOB);
- }
- src = new Text(data);
-
- } else {
- src = Text.EMPTY;
- }
-
- if (reuse) {
- mimeType = other.mimeType;
- displayMethod = other.displayMethod;
-
- } else if (src.getContent().length > 0 && FileMode.SYMLINK != mode) {
- mimeType = registry.getMimeType(path, src.getContent());
- if ("image".equals(mimeType.getMediaType())
- && registry.isSafeInline(mimeType)) {
- displayMethod = DisplayMethod.IMG;
- }
- }
-
- if (mode == FileMode.MISSING) {
- displayMethod = DisplayMethod.NONE;
- }
-
- dst.setMissingNewlineAtEnd(src.isMissingNewlineAtEnd());
- dst.setSize(src.size());
- } catch (IOException err) {
- throw new IOException("Cannot read " + within.name() + ":" + path, err);
- }
- }
-
- private TreeWalk find(final ObjectId within) throws MissingObjectException,
- IncorrectObjectTypeException, CorruptObjectException, IOException {
- if (path == null) {
- return null;
- }
- final RevWalk rw = new RevWalk(db);
- final RevTree tree = rw.parseTree(within);
- return TreeWalk.forPath(db, path, tree);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptFactory.java b/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptFactory.java
deleted file mode 100644
index 1b947b50a0..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/PatchScriptFactory.java
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.data.PatchScript;
-import com.google.gerrit.client.data.PatchScriptSettings;
-import com.google.gerrit.client.data.PatchScriptSettings.Whitespace;
-import com.google.gerrit.client.patches.CommentDetail;
-import com.google.gerrit.client.reviewdb.AccountGeneralPreferences;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.FileTypeRegistry;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListEntry;
-import com.google.gerrit.server.patch.PatchListKey;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-
-
-class PatchScriptFactory extends Handler<PatchScript> {
- interface Factory {
- PatchScriptFactory create(Patch.Key patchKey,
- @Assisted("patchSetA") PatchSet.Id patchSetA,
- @Assisted("patchSetB") PatchSet.Id patchSetB,
- PatchScriptSettings settings);
- }
-
- private static final Logger log =
- LoggerFactory.getLogger(PatchScriptFactory.class);
-
- private final GitRepositoryManager repoManager;
- private final FileTypeRegistry registry;
- private final PatchListCache patchListCache;
- private final ReviewDb db;
- private final ChangeControl.Factory changeControlFactory;
-
- private final Patch.Key patchKey;
- @Nullable
- private final PatchSet.Id psa;
- private final PatchSet.Id psb;
- private final PatchScriptSettings settings;
-
- private final PatchSet.Id patchSetId;
- private final Change.Id changeId;
-
- private Change change;
- private PatchSet patchSet;
- private Project.NameKey projectKey;
- private Repository git;
-
- private ChangeControl control;
-
- private ObjectId aId;
-
- private ObjectId bId;
-
- @Inject
- PatchScriptFactory(final GitRepositoryManager grm, final FileTypeRegistry ftr,
- final PatchListCache patchListCache, final ReviewDb db,
- final ChangeControl.Factory changeControlFactory,
- @Assisted final Patch.Key patchKey,
- @Assisted("patchSetA") @Nullable final PatchSet.Id patchSetA,
- @Assisted("patchSetB") final PatchSet.Id patchSetB,
- @Assisted final PatchScriptSettings settings) {
- this.repoManager = grm;
- this.registry = ftr;
- this.patchListCache = patchListCache;
- this.db = db;
- this.changeControlFactory = changeControlFactory;
-
- this.patchKey = patchKey;
- this.psa = patchSetA;
- this.psb = patchSetB;
- this.settings = settings;
-
- patchSetId = patchKey.getParentKey();
- changeId = patchSetId.getParentKey();
- }
-
- @Override
- public PatchScript call() throws OrmException, NoSuchChangeException {
- validatePatchSetId(psa);
- validatePatchSetId(psb);
-
- control = changeControlFactory.validateFor(changeId);
- change = control.getChange();
- patchSet = db.patchSets().get(patchSetId);
- if (patchSet == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- projectKey = change.getProject();
- aId = psa != null ? toObjectId(db, psa) : null;
- bId = toObjectId(db, psb);
-
- try {
- git = repoManager.openRepository(projectKey.get());
- } catch (RepositoryNotFoundException e) {
- log.error("Repository " + projectKey + " not found", e);
- throw new NoSuchChangeException(changeId, e);
- }
-
- final String fileName = patchKey.getFileName();
- try {
- final PatchList list = listFor(keyFor(settings.getWhitespace()));
- final PatchScriptBuilder b = newBuilder(list);
- final PatchListEntry contentWS = list.get(fileName);
- final CommentDetail comments = allComments(db);
-
- final PatchListEntry contentActual;
- if (settings.getWhitespace() == Whitespace.IGNORE_NONE) {
- contentActual = contentWS;
- } else {
- // If we are ignoring whitespace in some form, we still need to know
- // where the post-image differs so we can ensure the post-image lines
- // are still packed for the client to display.
- //
- contentActual = listFor(keyFor(Whitespace.IGNORE_NONE)).get(fileName);
- }
-
- try {
- return b.toPatchScript(contentWS, comments, contentActual);
- } catch (IOException e) {
- log.error("File content unavailable", e);
- throw new NoSuchChangeException(changeId, e);
- }
- } finally {
- git.close();
- }
- }
-
- private PatchListKey keyFor(final Whitespace whitespace) {
- return new PatchListKey(projectKey, aId, bId, whitespace);
- }
-
- private PatchList listFor(final PatchListKey key) {
- return patchListCache.get(key);
- }
-
- private PatchScriptBuilder newBuilder(final PatchList list)
- throws NoSuchChangeException {
- final PatchScriptSettings s = new PatchScriptSettings(settings);
-
- final int ctx = settings.getContext();
- if (ctx == AccountGeneralPreferences.WHOLE_FILE_CONTEXT)
- s.setContext(PatchScriptBuilder.MAX_CONTEXT);
- else if (0 <= ctx && ctx <= PatchScriptBuilder.MAX_CONTEXT)
- s.setContext(ctx);
- else
- throw new NoSuchChangeException(changeId);
-
- final PatchScriptBuilder b = new PatchScriptBuilder(registry);
- b.setRepository(git);
- b.setChange(change);
- b.setSettings(s);
- b.setTrees(list.getOldId(), list.getNewId());
- return b;
- }
-
- private ObjectId toObjectId(final ReviewDb db, final PatchSet.Id psId)
- throws OrmException, NoSuchChangeException {
- if (!changeId.equals(psId.getParentKey())) {
- throw new NoSuchChangeException(changeId);
- }
-
- final PatchSet ps = db.patchSets().get(psId);
- if (ps == null || ps.getRevision() == null
- || ps.getRevision().get() == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- try {
- return ObjectId.fromString(ps.getRevision().get());
- } catch (IllegalArgumentException e) {
- log.error("Patch set " + psId + " has invalid revision");
- throw new NoSuchChangeException(changeId, e);
- }
- }
-
- private void validatePatchSetId(final PatchSet.Id psId)
- throws NoSuchChangeException {
- if (psId == null) { // OK, means use base;
- } else if (changeId.equals(psId.getParentKey())) { // OK, same change;
- } else {
- throw new NoSuchChangeException(changeId);
- }
- }
-
- private CommentDetail allComments(final ReviewDb db) throws OrmException {
- final CommentDetail r = new CommentDetail(psa, psb);
- final String pn = patchKey.get();
- for (PatchLineComment p : db.patchComments().published(changeId, pn)) {
- r.include(p);
- }
-
- if (control.getCurrentUser() instanceof IdentifiedUser) {
- for (PatchLineComment p : db.patchComments().draft(changeId, pn,
- ((IdentifiedUser) control.getCurrentUser()).getAccountId())) {
- r.include(p);
- }
- }
- return r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/patch/SaveDraft.java b/src/main/java/com/google/gerrit/server/rpc/patch/SaveDraft.java
deleted file mode 100644
index c98904ddd7..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/patch/SaveDraft.java
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.patch;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.Patch;
-import com.google.gerrit.client.reviewdb.PatchLineComment;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-
-class SaveDraft extends Handler<PatchLineComment> {
- interface Factory {
- SaveDraft create(PatchLineComment comment);
- }
-
- private final ChangeControl.Factory changeControlFactory;
- private final ReviewDb db;
- private final IdentifiedUser currentUser;
-
- private final PatchLineComment comment;
-
- @Inject
- SaveDraft(final ChangeControl.Factory changeControlFactory,
- final ReviewDb db, final IdentifiedUser currentUser,
- @Assisted final PatchLineComment comment) {
- this.changeControlFactory = changeControlFactory;
- this.db = db;
- this.currentUser = currentUser;
-
- this.comment = comment;
- }
-
- @Override
- public PatchLineComment call() throws NoSuchChangeException, OrmException {
- if (comment.getStatus() != PatchLineComment.Status.DRAFT) {
- throw new IllegalStateException("Comment published");
- }
-
- final Patch.Key patchKey = comment.getKey().getParentKey();
- final PatchSet.Id patchSetId = patchKey.getParentKey();
- final Change.Id changeId = patchKey.getParentKey().getParentKey();
- changeControlFactory.validateFor(changeId);
- if (db.patchSets().get(patchSetId) == null) {
- throw new NoSuchChangeException(changeId);
- }
-
- final Account.Id me = currentUser.getAccountId();
- if (comment.getKey().get() == null) {
- if (comment.getLine() < 1) {
- throw new IllegalStateException("Comment line must be >= 1, not "
- + comment.getLine());
- }
-
- if (comment.getParentUuid() != null) {
- final PatchLineComment parent =
- db.patchComments().get(
- new PatchLineComment.Key(patchKey, comment.getParentUuid()));
- if (parent == null || parent.getSide() != comment.getSide()) {
- throw new IllegalStateException("Parent comment must be on same side");
- }
- }
-
- final PatchLineComment nc =
- new PatchLineComment(new PatchLineComment.Key(patchKey, ChangeUtil
- .messageUUID(db)), comment.getLine(), me, comment.getParentUuid());
- nc.setSide(comment.getSide());
- nc.setMessage(comment.getMessage());
- db.patchComments().insert(Collections.singleton(nc));
- return nc;
-
- } else {
- if (!me.equals(comment.getAuthor())) {
- throw new NoSuchChangeException(changeId);
- }
- comment.updated();
- db.patchComments().update(Collections.singleton(comment));
- return comment;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/AddBranch.java b/src/main/java/com/google/gerrit/server/rpc/project/AddBranch.java
deleted file mode 100644
index f9622d63a3..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/AddBranch.java
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.rpc.InvalidNameException;
-import com.google.gerrit.client.rpc.InvalidRevisionException;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.ObjectWalk;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.List;
-
-class AddBranch extends Handler<List<Branch>> {
- private static final Logger log = LoggerFactory.getLogger(AddBranch.class);
-
- interface Factory {
- AddBranch create(@Assisted Project.NameKey projectName,
- @Assisted("branchName") String branchName,
- @Assisted("startingRevision") String startingRevision);
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final ListBranches.Factory listBranchesFactory;
- private final IdentifiedUser identifiedUser;
- private final GitRepositoryManager repoManager;
- private final ReplicationQueue replication;
-
- private final Project.NameKey projectName;
- private final String branchName;
- private final String startingRevision;
-
- @Inject
- AddBranch(final ProjectControl.Factory projectControlFactory,
- final ListBranches.Factory listBranchesFactory,
- final IdentifiedUser identifiedUser, final GitRepositoryManager repoManager,
- final ReplicationQueue replication,
-
- @Assisted Project.NameKey projectName,
- @Assisted("branchName") String branchName,
- @Assisted("startingRevision") String startingRevision) {
- this.projectControlFactory = projectControlFactory;
- this.listBranchesFactory = listBranchesFactory;
- this.identifiedUser = identifiedUser;
- this.repoManager = repoManager;
- this.replication = replication;
-
- this.projectName = projectName;
- this.branchName = branchName;
- this.startingRevision = startingRevision;
- }
-
- @Override
- public List<Branch> call() throws NoSuchProjectException,
- InvalidNameException, InvalidRevisionException, IOException {
- final ProjectControl projectControl =
- projectControlFactory.validateFor(projectName, ProjectControl.OWNER
- | ProjectControl.VISIBLE);
-
- String refname = branchName;
- while (refname.startsWith("/")) {
- refname = refname.substring(1);
- }
- if (!refname.startsWith(Constants.R_REFS)) {
- refname = Constants.R_HEADS + refname;
- }
- if (!Repository.isValidRefName(refname)) {
- throw new InvalidNameException();
- }
- if (!projectControl.canCreateRef(refname)) {
- throw new IllegalStateException("Cannot create " + refname);
- }
-
- final Branch.NameKey name = new Branch.NameKey(projectName, refname);
- final Repository repo = repoManager.openRepository(projectName.get());
- try {
- final ObjectId revid = parseStartingRevision(repo);
- final RevWalk rw = verifyConnected(repo, revid);
-
- try {
- final RefUpdate u = repo.updateRef(refname);
- u.setExpectedOldObjectId(ObjectId.zeroId());
- u.setNewObjectId(revid);
- u.setRefLogIdent(identifiedUser.newPersonIdent());
- u.setRefLogMessage("created via web from " + startingRevision, false);
- final RefUpdate.Result result = u.update(rw);
- switch (result) {
- case FAST_FORWARD:
- case NEW:
- case NO_CHANGE:
- replication.scheduleUpdate(name.getParentKey(), refname);
- break;
- default: {
- final String msg =
- "Cannot create branch " + name + ": " + result.name();
- log.error(msg);
- throw new IOException(result.name());
- }
- }
- } catch (IOException err) {
- log.error("Cannot create branch " + name, err);
- throw err;
- }
- } finally {
- repo.close();
- }
-
- return listBranchesFactory.create(projectName).call();
- }
-
- private ObjectId parseStartingRevision(final Repository repo)
- throws InvalidRevisionException {
- try {
- final ObjectId revid = repo.resolve(startingRevision);
- if (revid == null) {
- throw new InvalidRevisionException();
- }
- return revid;
- } catch (IOException err) {
- log.error("Cannot resolve \"" + startingRevision + "\" in project \""
- + projectName + "\"", err);
- throw new InvalidRevisionException();
- }
- }
-
- private RevWalk verifyConnected(final Repository repo, final ObjectId revid)
- throws InvalidRevisionException {
- try {
- final ObjectWalk rw = new ObjectWalk(repo);
- try {
- rw.markStart(rw.parseCommit(revid));
- } catch (IncorrectObjectTypeException err) {
- throw new InvalidRevisionException();
- }
- for (final Ref r : repo.getAllRefs().values()) {
- try {
- rw.markUninteresting(rw.parseAny(r.getObjectId()));
- } catch (MissingObjectException err) {
- continue;
- }
- }
- rw.checkConnectivity();
- return rw;
- } catch (IncorrectObjectTypeException err) {
- throw new InvalidRevisionException();
- } catch (MissingObjectException err) {
- throw new InvalidRevisionException();
- } catch (IOException err) {
- log.error("Repository \"" + repo.getDirectory()
- + "\" may be corrupt; suggest running git fsck", err);
- throw new InvalidRevisionException();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/AddProjectRight.java b/src/main/java/com/google/gerrit/server/rpc/project/AddProjectRight.java
deleted file mode 100644
index debe3504cc..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/AddProjectRight.java
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.admin.ProjectDetail;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.account.NoSuchGroupException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-
-class AddProjectRight extends Handler<ProjectDetail> {
- interface Factory {
- AddProjectRight create(@Assisted Project.NameKey projectName,
- @Assisted ApprovalCategory.Id categoryId, @Assisted String groupName,
- @Assisted("min") short min, @Assisted("max") short max);
- }
-
- private final ProjectDetailFactory.Factory projectDetailFactory;
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final ReviewDb db;
- private final ApprovalTypes approvalTypes;
-
- private final Project.NameKey projectName;
- private final ApprovalCategory.Id categoryId;
- private final AccountGroup.NameKey groupName;
- private final short min;
- private final short max;
-
- @Inject
- AddProjectRight(final ProjectDetailFactory.Factory projectDetailFactory,
- final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final ReviewDb db,
- final ApprovalTypes approvalTypes,
-
- @Assisted final Project.NameKey projectName,
- @Assisted final ApprovalCategory.Id categoryId,
- @Assisted final String groupName, @Assisted("min") final short min,
- @Assisted("max") final short max) {
- this.projectDetailFactory = projectDetailFactory;
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.approvalTypes = approvalTypes;
- this.db = db;
-
- this.projectName = projectName;
- this.categoryId = categoryId;
- this.groupName = new AccountGroup.NameKey(groupName);
-
- if (min <= max) {
- this.min = min;
- this.max = max;
- } else {
- this.min = max;
- this.max = min;
- }
- }
-
- @Override
- public ProjectDetail call() throws NoSuchProjectException, OrmException,
- NoSuchGroupException {
- final ProjectControl projectControl =
- projectControlFactory.ownerFor(projectName);
-
- if (projectControl.getProjectState().isSpecialWildProject()
- && ApprovalCategory.OWN.equals(categoryId)) {
- // Giving out control of the WILD_PROJECT to other groups beyond
- // Administrators is dangerous. Having control over WILD_PROJECT
- // is about the same as having Administrator access as users are
- // able to affect grants in all projects on the system.
- //
- throw new IllegalArgumentException("Cannot give " + categoryId.get()
- + " on " + projectName + " " + groupName);
- }
-
- final ApprovalType at = approvalTypes.getApprovalType(categoryId);
- if (at == null || at.getValue(min) == null || at.getValue(max) == null) {
- throw new IllegalArgumentException("Invalid category " + categoryId
- + " or range " + min + ".." + max);
- }
-
- final AccountGroup group = db.accountGroups().get(groupName);
- if (group == null) {
- throw new NoSuchGroupException(groupName);
- }
-
- final ProjectRight.Key key =
- new ProjectRight.Key(projectName, categoryId, group.getId());
- ProjectRight pr = db.projectRights().get(key);
- if (pr == null) {
- pr = new ProjectRight(key);
- pr.setMinValue(min);
- pr.setMaxValue(max);
- db.projectRights().insert(Collections.singleton(pr));
- } else {
- pr.setMinValue(min);
- pr.setMaxValue(max);
- db.projectRights().update(Collections.singleton(pr));
- }
-
- projectCache.evict(projectControl.getProject());
- return projectDetailFactory.create(projectName).call();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/ChangeProjectSettings.java b/src/main/java/com/google/gerrit/server/rpc/project/ChangeProjectSettings.java
deleted file mode 100644
index c747739f57..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/ChangeProjectSettings.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.admin.ProjectDetail;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-
-class ChangeProjectSettings extends Handler<ProjectDetail> {
- interface Factory {
- ChangeProjectSettings create(@Assisted Project update);
- }
-
- private final ProjectDetailFactory.Factory projectDetailFactory;
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final ReviewDb db;
- private final GitRepositoryManager repoManager;
-
- private final Project update;
-
- @Inject
- ChangeProjectSettings(
- final ProjectDetailFactory.Factory projectDetailFactory,
- final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final ReviewDb db,
- final GitRepositoryManager grm,
- @Assisted final Project update) {
- this.projectDetailFactory = projectDetailFactory;
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.db = db;
- this.repoManager = grm;
-
- this.update = update;
- }
-
- @Override
- public ProjectDetail call() throws NoSuchProjectException, OrmException {
- final Project.NameKey projectName = update.getNameKey();
- final ProjectControl projectControl =
- projectControlFactory.ownerFor(projectName);
-
- final Project proj = db.projects().get(projectName);
- if (proj == null) {
- throw new NoSuchProjectException(projectName);
- }
-
- proj.copySettingsFrom(update);
- db.projects().update(Collections.singleton(proj));
- projectCache.evict(proj);
-
- if (!projectControl.getProjectState().isSpecialWildProject()) {
- repoManager.setProjectDescription(projectName.get(), update.getDescription());
- }
-
- return projectDetailFactory.create(projectName).call();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/DeleteBranches.java b/src/main/java/com/google/gerrit/server/rpc/project/DeleteBranches.java
deleted file mode 100644
index 5f47bb95d1..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/DeleteBranches.java
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-
-class DeleteBranches extends Handler<Set<Branch.NameKey>> {
- private static final Logger log =
- LoggerFactory.getLogger(DeleteBranches.class);
-
- interface Factory {
- DeleteBranches create(@Assisted Project.NameKey name,
- @Assisted Set<Branch.NameKey> toRemove);
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final GitRepositoryManager repoManager;
- private final ReplicationQueue replication;
-
- private final Project.NameKey projectName;
- private final Set<Branch.NameKey> toRemove;
-
- @Inject
- DeleteBranches(final ProjectControl.Factory projectControlFactory,
- final GitRepositoryManager repoManager,
- final ReplicationQueue replication,
-
- @Assisted Project.NameKey name, @Assisted Set<Branch.NameKey> toRemove) {
- this.projectControlFactory = projectControlFactory;
- this.repoManager = repoManager;
- this.replication = replication;
-
- this.projectName = name;
- this.toRemove = toRemove;
- }
-
- @Override
- public Set<Branch.NameKey> call() throws NoSuchProjectException,
- RepositoryNotFoundException {
- final ProjectControl projectControl =
- projectControlFactory.validateFor(projectName, ProjectControl.OWNER
- | ProjectControl.VISIBLE);
-
- for (Branch.NameKey k : toRemove) {
- if (!projectName.equals(k.getParentKey())) {
- throw new IllegalArgumentException("All keys must be from same project");
- }
- if (!projectControl.canDeleteRef(k.get())) {
- throw new IllegalStateException("Cannot delete " + k.getShortName());
- }
- }
-
- final Set<Branch.NameKey> deleted = new HashSet<Branch.NameKey>();
- final Repository r = repoManager.openRepository(projectName.get());
- try {
- for (final Branch.NameKey branchKey : toRemove) {
- final String refname = branchKey.get();
- final RefUpdate.Result result;
- try {
- final RefUpdate u = r.updateRef(refname);
- u.setForceUpdate(true);
- result = u.delete();
- } catch (IOException e) {
- log.error("Cannot delete " + branchKey, e);
- continue;
- }
-
- switch (result) {
- case NEW:
- case NO_CHANGE:
- case FAST_FORWARD:
- case FORCED:
- deleted.add(branchKey);
- replication.scheduleUpdate(projectName, refname);
- break;
-
- case REJECTED_CURRENT_BRANCH:
- log.warn("Cannot delete " + branchKey + ": " + result.name());
- break;
-
- default:
- log.error("Cannot delete " + branchKey + ": " + result.name());
- break;
- }
- }
- } finally {
- r.close();
- }
- return deleted;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/DeleteProjectRights.java b/src/main/java/com/google/gerrit/server/rpc/project/DeleteProjectRights.java
deleted file mode 100644
index 3cd398dbae..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/DeleteProjectRights.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.Collections;
-import java.util.Set;
-
-class DeleteProjectRights extends Handler<VoidResult> {
- interface Factory {
- DeleteProjectRights create(@Assisted Project.NameKey projectName,
- @Assisted Set<ProjectRight.Key> toRemove);
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final ProjectCache projectCache;
- private final ReviewDb db;
-
- private final Project.NameKey projectName;
- private final Set<ProjectRight.Key> toRemove;
-
- @Inject
- DeleteProjectRights(final ProjectControl.Factory projectControlFactory,
- final ProjectCache projectCache, final ReviewDb db,
-
- @Assisted final Project.NameKey projectName,
- @Assisted final Set<ProjectRight.Key> toRemove) {
- this.projectControlFactory = projectControlFactory;
- this.projectCache = projectCache;
- this.db = db;
-
- this.projectName = projectName;
- this.toRemove = toRemove;
- }
-
- @Override
- public VoidResult call() throws NoSuchProjectException, OrmException {
- final ProjectControl projectControl =
- projectControlFactory.ownerFor(projectName);
-
- for (final ProjectRight.Key k : toRemove) {
- if (!projectName.equals(k.getProjectNameKey())) {
- throw new IllegalArgumentException("All keys must be from same project");
- }
- }
-
- for (final ProjectRight.Key k : toRemove) {
- final ProjectRight m = db.projectRights().get(k);
- if (m != null) {
- db.projectRights().delete(Collections.singleton(m));
- }
- }
- projectCache.evict(projectControl.getProject());
- return VoidResult.INSTANCE;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/ListBranches.java b/src/main/java/com/google/gerrit/server/rpc/project/ListBranches.java
deleted file mode 100644
index 4dc12856c5..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/ListBranches.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-
-class ListBranches extends Handler<List<Branch>> {
- interface Factory {
- ListBranches create(@Assisted Project.NameKey name);
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final GitRepositoryManager repoManager;
-
- private final Project.NameKey projectName;
-
- @Inject
- ListBranches(final ProjectControl.Factory projectControlFactory,
- final GitRepositoryManager repoManager,
-
- @Assisted final Project.NameKey name) {
- this.projectControlFactory = projectControlFactory;
- this.repoManager = repoManager;
-
- this.projectName = name;
- }
-
- @Override
- public List<Branch> call() throws NoSuchProjectException,
- RepositoryNotFoundException {
- final ProjectControl projectControl =
- projectControlFactory.validateFor(projectName, ProjectControl.OWNER
- | ProjectControl.VISIBLE);
-
- final List<Branch> branches = new ArrayList<Branch>();
- final Repository db = repoManager.openRepository(projectName.get());
- try {
- final Map<String, Ref> all = db.getAllRefs();
-
- if (!all.containsKey(Constants.HEAD)) {
- // The branch pointed to by HEAD doesn't exist yet. Fake
- // that it exists by returning a Ref with no ObjectId.
- //
- try {
- final String head = db.getFullBranch();
- if (head != null && head.startsWith(Constants.R_REFS)) {
- all.put(Constants.HEAD, new Ref(Ref.Storage.LOOSE, Constants.HEAD,
- head, null));
- }
- } catch (IOException e) {
- // Ignore the failure reading HEAD.
- }
- }
-
- for (final Ref ref : all.values()) {
- final String name = ref.getName();
- if (name.startsWith(Constants.R_HEADS)) {
- final Branch b = new Branch(new Branch.NameKey(projectName, name));
- if (ref.getObjectId() != null) {
- b.setRevision(new RevId(ref.getObjectId().name()));
- }
- branches.add(b);
- }
- }
- } finally {
- db.close();
- }
- Collections.sort(branches, new Comparator<Branch>() {
- @Override
- public int compare(final Branch a, final Branch b) {
- return a.getName().compareTo(b.getName());
- }
- });
- return branches;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/OwnedProjects.java b/src/main/java/com/google/gerrit/server/rpc/project/OwnedProjects.java
deleted file mode 100644
index 573e090c41..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/OwnedProjects.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-
-class OwnedProjects extends Handler<List<Project>> {
- interface Factory {
- OwnedProjects create();
- }
-
- private final ProjectControl.Factory projectControlFactory;
- private final IdentifiedUser user;
- private final ReviewDb db;
-
- @Inject
- OwnedProjects(final ProjectControl.Factory projectControlFactory,
- final IdentifiedUser user, final ReviewDb db) {
- this.projectControlFactory = projectControlFactory;
- this.user = user;
- this.db = db;
- }
-
- @Override
- public List<Project> call() throws OrmException {
- final List<Project> result;
- if (user.isAdministrator()) {
- result = db.projects().all().toList();
-
- } else {
- final HashSet<Project.NameKey> seen = new HashSet<Project.NameKey>();
- result = new ArrayList<Project>();
- for (final AccountGroup.Id groupId : user.getEffectiveGroups()) {
- for (final ProjectRight r : db.projectRights().byCategoryGroup(
- ApprovalCategory.OWN, groupId)) {
- final Project.NameKey name = r.getProjectNameKey();
- if (!seen.add(name)) {
- continue;
- }
- try {
- ProjectControl c = projectControlFactory.ownerFor(name);
- result.add(c.getProject());
- } catch (NoSuchProjectException e) {
- continue;
- }
- }
- }
- }
- Collections.sort(result, new Comparator<Project>() {
- public int compare(final Project a, final Project b) {
- return a.getName().compareTo(b.getName());
- }
- });
- return result;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/ProjectAdminServiceImpl.java b/src/main/java/com/google/gerrit/server/rpc/project/ProjectAdminServiceImpl.java
deleted file mode 100644
index c095be83a6..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/ProjectAdminServiceImpl.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.admin.ProjectAdminService;
-import com.google.gerrit.client.admin.ProjectDetail;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.google.gwtjsonrpc.client.VoidResult;
-import com.google.inject.Inject;
-
-import java.util.List;
-import java.util.Set;
-
-class ProjectAdminServiceImpl implements ProjectAdminService {
- private final AddBranch.Factory addBranchFactory;
- private final AddProjectRight.Factory addProjectRightFactory;
- private final ChangeProjectSettings.Factory changeProjectSettingsFactory;
- private final DeleteBranches.Factory deleteBranchesFactory;
- private final DeleteProjectRights.Factory deleteProjectRightsFactory;
- private final ListBranches.Factory listBranchesFactory;
- private final OwnedProjects.Factory ownedProjectsFactory;
- private final ProjectDetailFactory.Factory projectDetailFactory;
-
- @Inject
- ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory,
- final AddProjectRight.Factory addProjectRightFactory,
- final ChangeProjectSettings.Factory changeProjectSettingsFactory,
- final DeleteBranches.Factory deleteBranchesFactory,
- final DeleteProjectRights.Factory deleteProjectRightFactory,
- final ListBranches.Factory listBranchesFactory,
- final OwnedProjects.Factory ownedProjectsFactory,
- final ProjectDetailFactory.Factory projectDetailFactory) {
- this.addBranchFactory = addBranchFactory;
- this.addProjectRightFactory = addProjectRightFactory;
- this.changeProjectSettingsFactory = changeProjectSettingsFactory;
- this.deleteBranchesFactory = deleteBranchesFactory;
- this.deleteProjectRightsFactory = deleteProjectRightFactory;
- this.listBranchesFactory = listBranchesFactory;
- this.ownedProjectsFactory = ownedProjectsFactory;
- this.projectDetailFactory = projectDetailFactory;
- }
-
- public void ownedProjects(final AsyncCallback<List<Project>> callback) {
- ownedProjectsFactory.create().to(callback);
- }
-
- public void projectDetail(final Project.NameKey projectName,
- final AsyncCallback<ProjectDetail> callback) {
- projectDetailFactory.create(projectName).to(callback);
- }
-
- public void changeProjectSettings(final Project update,
- final AsyncCallback<ProjectDetail> callback) {
- changeProjectSettingsFactory.create(update).to(callback);
- }
-
- public void deleteRight(final Project.NameKey projectName,
- final Set<ProjectRight.Key> toRemove,
- final AsyncCallback<VoidResult> callback) {
- deleteProjectRightsFactory.create(projectName, toRemove).to(callback);
- }
-
- public void addRight(final Project.NameKey projectName,
- final ApprovalCategory.Id categoryId, final String groupName,
- final short min, final short max,
- final AsyncCallback<ProjectDetail> callback) {
- addProjectRightFactory.create(projectName, categoryId, groupName, min, max)
- .to(callback);
- }
-
- public void listBranches(final Project.NameKey projectName,
- final AsyncCallback<List<Branch>> callback) {
- listBranchesFactory.create(projectName).to(callback);
- }
-
- public void deleteBranch(final Project.NameKey projectName,
- final Set<Branch.NameKey> toRemove,
- final AsyncCallback<Set<Branch.NameKey>> callback) {
- deleteBranchesFactory.create(projectName, toRemove).to(callback);
- }
-
- public void addBranch(final Project.NameKey projectName,
- final String branchName, final String startingRevision,
- final AsyncCallback<List<Branch>> callback) {
- addBranchFactory.create(projectName, branchName, startingRevision).to(
- callback);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/ProjectDetailFactory.java b/src/main/java/com/google/gerrit/server/rpc/project/ProjectDetailFactory.java
deleted file mode 100644
index 7c5f00fd68..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/ProjectDetailFactory.java
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.client.admin.ProjectDetail;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.rpc.Handler;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-class ProjectDetailFactory extends Handler<ProjectDetail> {
- interface Factory {
- ProjectDetailFactory create(@Assisted Project.NameKey name);
- }
-
- private final ApprovalTypes approvalTypes;
- private final GroupCache groupCache;
- private final ProjectControl.Factory projectControlFactory;
-
- private final Project.NameKey projectName;
- private Map<AccountGroup.Id, AccountGroup> groups;
-
- @Inject
- ProjectDetailFactory(final ApprovalTypes approvalTypes,
- final GroupCache groupCache,
- final ProjectControl.Factory projectControlFactory,
-
- @Assisted final Project.NameKey name) {
- this.approvalTypes = approvalTypes;
- this.groupCache = groupCache;
- this.projectControlFactory = projectControlFactory;
-
- this.projectName = name;
- }
-
- @Override
- public ProjectDetail call() throws NoSuchProjectException {
- final ProjectState projectState =
- projectControlFactory.validateFor(projectName,
- ProjectControl.OWNER | ProjectControl.VISIBLE).getProjectState();
-
- final ProjectDetail detail = new ProjectDetail();
- detail.setProject(projectState.getProject());
-
- groups = new HashMap<AccountGroup.Id, AccountGroup>();
- final List<ProjectRight> rights = new ArrayList<ProjectRight>();
- for (final ProjectRight p : projectState.getLocalRights()) {
- rights.add(p);
- wantGroup(p.getAccountGroupId());
- }
- for (final ProjectRight p : projectState.getInheritedRights()) {
- rights.add(p);
- wantGroup(p.getAccountGroupId());
- }
- loadGroups();
-
- Collections.sort(rights, new Comparator<ProjectRight>() {
- @Override
- public int compare(final ProjectRight a, final ProjectRight b) {
- int rc = categoryOf(a).compareTo(categoryOf(b));
- if (rc == 0) {
- rc = groupOf(a).compareTo(groupOf(b));
- }
- return rc;
- }
-
- private String categoryOf(final ProjectRight r) {
- final ApprovalType type =
- approvalTypes.getApprovalType(r.getApprovalCategoryId());
- if (type == null) {
- return r.getApprovalCategoryId().get();
- }
- return type.getCategory().getName();
- }
-
- private String groupOf(final ProjectRight r) {
- return groups.get(r.getAccountGroupId()).getName();
- }
- });
-
- detail.setRights(rights);
- detail.setGroups(groups);
- return detail;
- }
-
- private void wantGroup(final AccountGroup.Id id) {
- groups.put(id, null);
- }
-
- private void loadGroups() {
- final Set<AccountGroup.Id> toGet = groups.keySet();
- groups = new HashMap<AccountGroup.Id, AccountGroup>();
- for (AccountGroup.Id groupId : toGet) {
- groups.put(groupId, groupCache.get(groupId));
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/rpc/project/ProjectModule.java b/src/main/java/com/google/gerrit/server/rpc/project/ProjectModule.java
deleted file mode 100644
index 3cfc35fe8a..0000000000
--- a/src/main/java/com/google/gerrit/server/rpc/project/ProjectModule.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.rpc.project;
-
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.http.RpcServletModule;
-import com.google.gerrit.server.rpc.UiRpcModule;
-
-public class ProjectModule extends RpcServletModule {
- public ProjectModule() {
- super(UiRpcModule.PREFIX);
- }
-
- @Override
- protected void configureServlets() {
- install(new FactoryModule() {
- @Override
- protected void configure() {
- factory(AddBranch.Factory.class);
- factory(AddProjectRight.Factory.class);
- factory(ChangeProjectSettings.Factory.class);
- factory(DeleteBranches.Factory.class);
- factory(DeleteProjectRights.Factory.class);
- factory(ListBranches.Factory.class);
- factory(OwnedProjects.Factory.class);
- factory(ProjectDetailFactory.Factory.class);
- }
- });
- rpc(ProjectAdminServiceImpl.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/AdminCommand.java b/src/main/java/com/google/gerrit/server/ssh/AdminCommand.java
deleted file mode 100644
index e8b5fdb7bc..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/AdminCommand.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Annotation tagged on a concrete Command that requires administrator access.
- * <p>
- * Currently this annotation is only enforced by DispatchCommand after it has
- * created the command object, but before it populates it or starts execution.
- */
-@Target( {ElementType.TYPE})
-@Retention(RUNTIME)
-public @interface AdminCommand {
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/BaseCommand.java b/src/main/java/com/google/gerrit/server/ssh/BaseCommand.java
deleted file mode 100644
index ab6254466e..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/BaseCommand.java
+++ /dev/null
@@ -1,454 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.pgm.CmdLineParser;
-import com.google.gerrit.server.RequestCleanup;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.ssh.SshScopes.Context;
-import com.google.inject.Inject;
-
-import org.apache.sshd.common.SshException;
-import org.apache.sshd.server.CommandFactory.Command;
-import org.apache.sshd.server.CommandFactory.ExitCallback;
-import org.apache.sshd.server.session.ServerSession;
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.Option;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-
-public abstract class BaseCommand implements Command {
- private static final Logger log = LoggerFactory.getLogger(BaseCommand.class);
- public static final String ENC = "UTF-8";
-
- @Option(name = "--help", usage = "display this help text", aliases = {"-h"})
- private boolean help;
-
- protected InputStream in;
- protected OutputStream out;
- protected OutputStream err;
-
- private ExitCallback exit;
-
- @Inject
- private CmdLineParser.Factory cmdLineParserFactory;
-
- @Inject
- private RequestCleanup cleanup;
-
- /** Text of the command line which lead up to invoking this instance. */
- protected String commandPrefix = "";
-
- /** Unparsed rest of the command line. */
- protected String commandLine = "";
-
- public void setInputStream(final InputStream in) {
- this.in = in;
- }
-
- public void setOutputStream(final OutputStream out) {
- this.out = out;
- }
-
- public void setErrorStream(final OutputStream err) {
- this.err = err;
- }
-
- public void setExitCallback(final ExitCallback callback) {
- this.exit = callback;
- }
-
- public void setCommandPrefix(final String prefix) {
- this.commandPrefix = prefix;
- }
-
- /**
- * Set the command line to be evaluated by this command.
- * <p>
- * If this command is being invoked from a higher level
- * {@link DispatchCommand} then only the portion after the command name (that
- * is, the arguments) is supplied.
- *
- * @param line the command line received from the client.
- */
- public void setCommandLine(final String line) {
- this.commandLine = line;
- }
-
- /**
- * Pass all state into the command, then run its start method.
- * <p>
- * This method copies all critical state, like the input and output streams,
- * into the supplied command. The caller must still invoke {@code cmd.start()}
- * if wants to pass control to the command.
- *
- * @param cmd the command that will receive the current state.
- */
- protected void provideStateTo(final Command cmd) {
- cmd.setInputStream(in);
- cmd.setOutputStream(out);
- cmd.setErrorStream(err);
- cmd.setExitCallback(exit);
- }
-
- /**
- * Parses the command line argument, injecting parsed values into fields.
- * <p>
- * This method must be explicitly invoked to cause a parse. When parsing,
- * arguments are split out of and read from the {@link #commandLine} field.
- *
- * @throws Failure if the command line arguments were invalid.
- * @see Option
- * @see Argument
- */
- protected void parseCommandLine() throws Failure {
- final List<String> list = new ArrayList<String>();
- boolean inquote = false;
- StringBuilder r = new StringBuilder();
- for (int ip = 0; ip < commandLine.length();) {
- final char b = commandLine.charAt(ip++);
- switch (b) {
- case '\t':
- case ' ':
- if (inquote)
- r.append(b);
- else if (r.length() > 0) {
- list.add(r.toString());
- r = new StringBuilder();
- }
- continue;
- case '\'':
- inquote = !inquote;
- continue;
- case '\\':
- if (inquote || ip == commandLine.length())
- r.append(b); // literal within a quote
- else
- r.append(commandLine.charAt(ip++));
- continue;
- default:
- r.append(b);
- continue;
- }
- }
- if (r.length() > 0) {
- list.add(r.toString());
- }
-
- final CmdLineParser clp = newCmdLineParser();
- try {
- clp.parseArgument(list.toArray(new String[list.size()]));
- } catch (IllegalArgumentException err) {
- if (!help) {
- throw new UnloggedFailure(1, "fatal: " + err.getMessage());
- }
- } catch (CmdLineException err) {
- if (!help) {
- throw new UnloggedFailure(1, "fatal: " + err.getMessage());
- }
- }
-
- if (help) {
- final StringWriter msg = new StringWriter();
- msg.write(commandPrefix);
- clp.printSingleLineUsage(msg, null);
- msg.write('\n');
-
- msg.write('\n');
- clp.printUsage(msg, null);
- msg.write('\n');
- throw new UnloggedFailure(1, msg.toString());
- }
- }
-
- /** Construct a new parser for this command's received command line. */
- protected CmdLineParser newCmdLineParser() {
- return cmdLineParserFactory.create(this);
- }
-
- /**
- * Spawn a function into its own thread.
- * <p>
- * Typically this should be invoked within {@link Command#start()}, such as:
- *
- * <pre>
- * startThread(new Runnable() {
- * public void run() {
- * runImp();
- * }
- * });
- * </pre>
- *
- * @param thunk the runnable to execute on the thread, performing the
- * command's logic.
- */
- protected void startThread(final Runnable thunk) {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- thunk.run();
- }
- });
- }
-
- /**
- * Spawn a function into its own thread.
- * <p>
- * Typically this should be invoked within {@link Command#start()}, such as:
- *
- * <pre>
- * startThread(new CommandRunnable() {
- * public void run() throws Exception {
- * runImp();
- * }
- * });
- * </pre>
- * <p>
- * If the function throws an exception, it is translated to a simple message
- * for the client, a non-zero exit code, and the stack trace is logged.
- *
- * @param thunk the runnable to execute on the thread, performing the
- * command's logic.
- */
- protected void startThread(final CommandRunnable thunk) {
- final Context context = SshScopes.getContext();
- final List<Command> active = context.session.getAttribute(SshUtil.ACTIVE);
- final Command cmd = this;
- new Thread(threadName()) {
- @Override
- public void run() {
- int rc = 0;
- try {
- synchronized (active) {
- active.add(cmd);
- }
- SshScopes.current.set(context);
- try {
- thunk.run();
- } catch (NoSuchProjectException e) {
- throw new UnloggedFailure(1, e.getMessage() + " no such project");
- } catch (NoSuchChangeException e) {
- throw new UnloggedFailure(1, e.getMessage() + " no such change");
- }
- out.flush();
- err.flush();
- } catch (Throwable e) {
- try {
- out.flush();
- } catch (Throwable e2) {
- }
- try {
- err.flush();
- } catch (Throwable e2) {
- }
- rc = handleError(e);
- } finally {
- synchronized (active) {
- active.remove(cmd);
- }
- onExit(rc);
- }
- }
- }.start();
- }
-
- /**
- * Terminate this command and return a result code to the remote client.
- * <p>
- * Commands should invoke this at most once. Once invoked, the command may
- * lose access to request based resources as any callbacks previously
- * registered with {@link RequestCleanup} will fire.
- *
- * @param rc exit code for the remote client.
- */
- protected void onExit(final int rc) {
- exit.onExit(rc);
- cleanup.run();
- }
-
- /** Wrap the supplied output stream in a UTF-8 encoded PrintWriter. */
- protected static PrintWriter toPrintWriter(final OutputStream o) {
- try {
- return new PrintWriter(new BufferedWriter(new OutputStreamWriter(o, ENC)));
- } catch (UnsupportedEncodingException e) {
- // Our default encoding is required by the specifications for the
- // runtime APIs, this should never, ever happen.
- //
- throw new RuntimeException("JVM lacks " + ENC + " encoding", e);
- }
- }
-
- private String threadName() {
- final ServerSession session = SshScopes.getContext().session;
- final String who = session.getUsername();
- final Account.Id id = session.getAttribute(SshUtil.CURRENT_ACCOUNT);
- return "SSH " + getFullCommandLine() + " / " + who + " " + id;
- }
-
- private int handleError(final Throwable e) {
- if (e.getClass() == IOException.class
- && "Pipe closed".equals(e.getMessage())) {
- // This is sshd telling us the client just dropped off while
- // we were waiting for a read or a write to complete. Either
- // way its not really a fatal error. Don't log it.
- //
- return 127;
- }
-
- if (e.getClass() == SshException.class
- && "Already closed".equals(e.getMessage())) {
- // This is sshd telling us the client just dropped off while
- // we were waiting for a read or a write to complete. Either
- // way its not really a fatal error. Don't log it.
- //
- return 127;
- }
-
- if (e instanceof UnloggedFailure) {
- } else {
- final ServerSession session = SshScopes.getContext().session;
- final StringBuilder m = new StringBuilder();
- m.append("Internal server error (");
- m.append("user ");
- m.append(session.getUsername());
- m.append(" account ");
- m.append(session.getAttribute(SshUtil.CURRENT_ACCOUNT));
- m.append(") during ");
- m.append(getFullCommandLine());
- log.error(m.toString(), e);
- }
-
- if (e instanceof Failure) {
- final Failure f = (Failure) e;
- try {
- err.write((f.getMessage() + "\n").getBytes(ENC));
- err.flush();
- } catch (IOException e2) {
- } catch (Throwable e2) {
- log.warn("Cannot send failure message to client", e2);
- }
- return f.exitCode;
-
- } else {
- try {
- err.write("fatal: internal server error\n".getBytes(ENC));
- err.flush();
- } catch (IOException e2) {
- } catch (Throwable e2) {
- log.warn("Cannot send internal server error message to client", e2);
- }
- return 128;
- }
- }
-
- @Override
- public String toString() {
- return getFullCommandLine();
- }
-
- private String getFullCommandLine() {
- if (commandPrefix.isEmpty())
- return commandLine;
- else if (commandLine.isEmpty())
- return commandPrefix;
- else
- return commandPrefix + " " + commandLine;
- }
-
- /** Runnable function which can throw an exception. */
- public static interface CommandRunnable {
- public void run() throws Exception;
- }
-
- /** Thrown from {@link CommandRunnable#run()} with client message and code. */
- public static class Failure extends Exception {
- private static final long serialVersionUID = 1L;
-
- final int exitCode;
-
- /**
- * Create a new failure.
- *
- * @param exitCode exit code to return the client, which indicates the
- * failure status of this command. Should be between 1 and 255,
- * inclusive.
- * @param msg message to also send to the client's stderr.
- */
- public Failure(final int exitCode, final String msg) {
- this(exitCode, msg, null);
- }
-
- /**
- * Create a new failure.
- *
- * @param exitCode exit code to return the client, which indicates the
- * failure status of this command. Should be between 1 and 255,
- * inclusive.
- * @param msg message to also send to the client's stderr.
- * @param why stack trace to include in the server's log, but is not sent to
- * the client's stderr.
- */
- public Failure(final int exitCode, final String msg, final Throwable why) {
- super(msg, why);
- this.exitCode = exitCode;
- }
- }
-
- /** Thrown from {@link CommandRunnable#run()} with client message and code. */
- public static class UnloggedFailure extends Failure {
- private static final long serialVersionUID = 1L;
-
- /**
- * Create a new failure.
- *
- * @param exitCode exit code to return the client, which indicates the
- * failure status of this command. Should be between 1 and 255,
- * inclusive.
- * @param msg message to also send to the client's stderr.
- */
- public UnloggedFailure(final int exitCode, final String msg) {
- this(exitCode, msg, null);
- }
-
- /**
- * Create a new failure.
- *
- * @param exitCode exit code to return the client, which indicates the
- * failure status of this command. Should be between 1 and 255,
- * inclusive.
- * @param msg message to also send to the client's stderr.
- * @param why stack trace to include in the server's log, but is not sent to
- * the client's stderr.
- */
- public UnloggedFailure(final int exitCode, final String msg,
- final Throwable why) {
- super(exitCode, msg, why);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/CommandFactoryProvider.java b/src/main/java/com/google/gerrit/server/ssh/CommandFactoryProvider.java
deleted file mode 100644
index 09dce87be9..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/CommandFactoryProvider.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.server.ssh.SshScopes.Context;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.CommandFactory.Command;
-import org.apache.sshd.server.CommandFactory.ExitCallback;
-import org.apache.sshd.server.CommandFactory.SessionAware;
-import org.apache.sshd.server.session.ServerSession;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Creates a CommandFactory using commands registered by {@link CommandModule}.
- */
-class CommandFactoryProvider implements Provider<CommandFactory> {
- private final DispatchCommandProvider dispatcher;
-
- @Inject
- CommandFactoryProvider(
- @CommandName(Commands.ROOT) final DispatchCommandProvider d) {
- dispatcher = d;
- }
-
- @Override
- public CommandFactory get() {
- return new CommandFactory() {
- public Command createCommand(final String requestCommand) {
- return new Trampoline(requestCommand);
- }
- };
- }
-
- private class Trampoline implements Command, SessionAware {
- private final String commandLine;
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback exit;
- private ServerSession session;
-
- Trampoline(final String cmdLine) {
- commandLine = cmdLine;
- }
-
- public void setInputStream(final InputStream in) {
- this.in = in;
- }
-
- public void setOutputStream(final OutputStream out) {
- this.out = out;
- }
-
- public void setErrorStream(final OutputStream err) {
- this.err = err;
- }
-
- public void setExitCallback(final ExitCallback callback) {
- this.exit = callback;
- }
-
- public void setSession(final ServerSession session) {
- this.session = session;
- }
-
- public void start() throws IOException {
- final Context old = SshScopes.current.get();
- try {
- SshScopes.current.set(new Context(session));
- final DispatchCommand c = dispatcher.get();
- c.setCommandLine(commandLine);
- c.setInputStream(in);
- c.setOutputStream(out);
- c.setErrorStream(err);
- c.setExitCallback(exit);
- c.start();
- } finally {
- SshScopes.current.set(old);
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/CommandModule.java b/src/main/java/com/google/gerrit/server/ssh/CommandModule.java
deleted file mode 100644
index 5bb639b7a2..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/CommandModule.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.binder.LinkedBindingBuilder;
-
-import org.apache.sshd.server.CommandFactory.Command;
-
-/** Module to register commands in the SSH daemon. */
-public abstract class CommandModule extends AbstractModule {
- /**
- * Configure a command to be invoked by name.
- *
- * @param name the name of the command the client will provide in order to
- * call the command.
- * @return a binding that must be bound to a non-singleton provider for a
- * {@link Command} object.
- */
- protected LinkedBindingBuilder<Command> command(final String name) {
- return bind(Commands.key(name));
- }
-
- /**
- * Configure a command to be invoked by name.
- *
- * @param name the name of the command the client will provide in order to
- * call the command.
- * @return a binding that must be bound to a non-singleton provider for a
- * {@link Command} object.
- */
- protected LinkedBindingBuilder<Command> command(final CommandName name) {
- return bind(Commands.key(name));
- }
-
- /**
- * Configure a command to be invoked by name.
- *
- *@param parent context of the parent command, that this command is a
- * subcommand of.
- * @param name the name of the command the client will provide in order to
- * call the command.
- * @return a binding that must be bound to a non-singleton provider for a
- * {@link Command} object.
- */
- protected LinkedBindingBuilder<Command> command(final CommandName parent,
- final String name) {
- return bind(Commands.key(parent, name));
- }
-
- /**
- * Alias one command to another.
- *
- * @param from the new command name that when called will actually delegate to
- * {@code to}'s implementation.
- * @param to name of an already registered command that will perform the
- * action when {@code from} is invoked by a client.
- */
- protected void alias(final String from, final String to) {
- bind(Commands.key(from)).to(Commands.key(to));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/CommandName.java b/src/main/java/com/google/gerrit/server/ssh/CommandName.java
deleted file mode 100644
index 10cd5e1353..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/CommandName.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import com.google.inject.BindingAnnotation;
-
-import java.lang.annotation.Retention;
-
-/**
- * Name of a command registered in an SSH daemon.
- * <p>
- * Use {@link Commands#key(String)} to construct a key for a command name.
- *
- * @see CommandModule#command(String)
- * @see Commands#key(String)
- */
-@Retention(RUNTIME)
-@BindingAnnotation
-public @interface CommandName {
- String value();
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/Commands.java b/src/main/java/com/google/gerrit/server/ssh/Commands.java
deleted file mode 100644
index 25d7ed2b60..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/Commands.java
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.inject.Key;
-
-import org.apache.sshd.server.CommandFactory;
-
-import java.lang.annotation.Annotation;
-
-/** Utilities to support {@link CommandName} construction. */
-public class Commands {
- /** Magic value signaling the top level. */
- public static final String ROOT = "";
-
- /** Magic value signaling the top level. */
- public static final CommandName CMD_ROOT = named(ROOT);
-
- public static Key<CommandFactory.Command> key(final String name) {
- return key(named(name));
- }
-
- public static Key<CommandFactory.Command> key(final CommandName name) {
- return Key.get(CommandFactory.Command.class, name);
- }
-
- public static Key<CommandFactory.Command> key(final CommandName parent,
- final String name) {
- return Key.get(CommandFactory.Command.class, named(parent, name));
- }
-
- /** Create a CommandName annotation for the supplied name. */
- public static CommandName named(final String name) {
- return new CommandName() {
- @Override
- public String value() {
- return name;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return CommandName.class;
- }
-
- @Override
- public int hashCode() {
- // This is specified in java.lang.Annotation.
- return (127 * "value".hashCode()) ^ value().hashCode();
- }
-
- @Override
- public boolean equals(final Object obj) {
- return obj instanceof CommandName
- && value().equals(((CommandName) obj).value());
- }
-
- @Override
- public String toString() {
- return "@" + CommandName.class.getName() + "(value=" + value() + ")";
- }
- };
- }
-
- /** Create a CommandName annotation for the supplied name. */
- public static CommandName named(final CommandName parent, final String name) {
- return new NestedCommandNameImpl(parent, name);
- }
-
- /** Return the name of this command, possibly including any parents. */
- public static String nameOf(final CommandName name) {
- if (name instanceof NestedCommandNameImpl) {
- return nameOf(((NestedCommandNameImpl) name).parent) + " " + name.value();
- }
- return name.value();
- }
-
- /** Is the second command a direct child of the first command? */
- public static boolean isChild(final CommandName parent, final CommandName name) {
- if (name instanceof NestedCommandNameImpl) {
- return parent.equals(((NestedCommandNameImpl) name).parent);
- }
- if (parent == CMD_ROOT) {
- return true;
- }
- return false;
- }
-
- private static final class NestedCommandNameImpl implements CommandName {
- private final CommandName parent;
- private final String name;
-
- NestedCommandNameImpl(final CommandName parent, final String name) {
- this.parent = parent;
- this.name = name;
- }
-
- @Override
- public String value() {
- return name;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return CommandName.class;
- }
-
- @Override
- public int hashCode() {
- return parent.hashCode() * 31 + value().hashCode();
- }
-
- @Override
- public boolean equals(final Object obj) {
- return obj instanceof NestedCommandNameImpl
- && parent.equals(((NestedCommandNameImpl) obj).parent)
- && value().equals(((NestedCommandNameImpl) obj).value());
- }
-
- @Override
- public String toString() {
- return "CommandName[" + nameOf(this) + "]";
- }
- }
-
- private Commands() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/DatabasePubKeyAuth.java b/src/main/java/com/google/gerrit/server/ssh/DatabasePubKeyAuth.java
deleted file mode 100644
index afb82b0789..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/DatabasePubKeyAuth.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.session.ServerSession;
-
-import java.security.PublicKey;
-
-/**
- * Authenticates by public key through {@link AccountSshKey} entities.
- * <p>
- * The username supplied by the client must be the user's preferred email
- * address, as listed in their Account entity. Only keys listed under that
- * account as authorized keys are permitted to access the account.
- */
-@Singleton
-class DatabasePubKeyAuth implements PublickeyAuthenticator {
- private final SshKeyCache sshKeyCache;
- private final SchemaFactory<ReviewDb> schema;
-
- @Inject
- DatabasePubKeyAuth(final SshKeyCache skc, final SchemaFactory<ReviewDb> sf) {
- sshKeyCache = skc;
- schema = sf;
- }
-
- public boolean hasKey(final String username, final PublicKey suppliedKey,
- final ServerSession session) {
- final Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
- final SshKeyCacheEntry key = find(keyList, suppliedKey);
- if (key == null) {
- return false;
- }
-
- // Double check that all of the keys are for the same user account.
- // This should have been true when the cache factory method loaded
- // the list into memory, but we want to be extra paranoid about our
- // security check to ensure there aren't two users sharing the same
- // user name on the server.
- //
- for (final SshKeyCacheEntry otherKey : keyList) {
- if (!key.getAccount().equals(otherKey.getAccount())) {
- return false;
- }
- }
-
- key.updateLastUsed(schema);
- session.setAttribute(SshUtil.CURRENT_ACCOUNT, key.getAccount());
- return true;
- }
-
- private SshKeyCacheEntry find(final Iterable<SshKeyCacheEntry> keyList,
- final PublicKey suppliedKey) {
- for (final SshKeyCacheEntry k : keyList) {
- if (k.match(suppliedKey)) {
- return k;
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/DispatchCommand.java b/src/main/java/com/google/gerrit/server/ssh/DispatchCommand.java
deleted file mode 100644
index aac3d5f63a..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/DispatchCommand.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.server.CurrentUser;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.assistedinject.Assisted;
-
-import org.apache.sshd.server.CommandFactory.Command;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.util.Map;
-
-/**
- * Command that dispatches to a subcommand from its command table.
- */
-final class DispatchCommand extends BaseCommand {
- interface Factory {
- DispatchCommand create(String prefix, Map<String, Provider<Command>> map);
- }
-
- private final Provider<CurrentUser> currentUser;
- private final String prefix;
- private final Map<String, Provider<Command>> commands;
-
- @Inject
- DispatchCommand(final Provider<CurrentUser> cu, @Assisted final String pfx,
- @Assisted final Map<String, Provider<Command>> all) {
- currentUser = cu;
- prefix = pfx;
- commands = all;
- }
-
- @Override
- public void start() throws IOException {
- if (commandLine.isEmpty()) {
- usage();
- return;
- }
-
- final String name, args;
- int sp = commandLine.indexOf(' ');
- if (0 < sp) {
- name = commandLine.substring(0, sp);
- while (Character.isWhitespace(commandLine.charAt(sp))) {
- sp++;
- }
- args = commandLine.substring(sp);
- } else {
- name = commandLine;
- args = "";
- }
-
- if (name.equals("help") || name.equals("--help") || name.equals("-h")) {
- usage();
- return;
- }
-
- final Provider<Command> p = commands.get(name);
- if (p != null) {
- final Command cmd = p.get();
- if (cmd.getClass().getAnnotation(AdminCommand.class) != null) {
- final CurrentUser u = currentUser.get();
- if (!u.isAdministrator()) {
- err.write("fatal: Not a Gerrit administrator\n".getBytes(ENC));
- err.flush();
- onExit(1);
- return;
- }
- }
-
- provideStateTo(cmd);
- if (cmd instanceof BaseCommand) {
- final BaseCommand bc = (BaseCommand) cmd;
- if (commandPrefix.isEmpty())
- bc.setCommandPrefix(name);
- else
- bc.setCommandPrefix(commandPrefix + " " + name);
- bc.setCommandLine(args);
- }
- cmd.start();
- } else {
- final String msg = prefix + ": " + name + ": not found\n";
- err.write(msg.getBytes(ENC));
- err.flush();
- onExit(127);
- }
- }
-
- private void usage() throws IOException, UnsupportedEncodingException {
- final StringBuilder usage = new StringBuilder();
- if (prefix.indexOf(' ') < 0) {
- usage.append("usage: " + prefix + " COMMAND [ARGS]\n");
- }
- usage.append("\n");
- usage.append("Available commands of " + prefix + " are:\n");
- usage.append("\n");
- for (Map.Entry<String, Provider<Command>> e : commands.entrySet()) {
- usage.append(" ");
- usage.append(e.getKey());
- usage.append("\n");
- }
- usage.append("\n");
-
- usage.append("See '");
- if (prefix.indexOf(' ') < 0) {
- usage.append(prefix);
- usage.append(' ');
- }
- usage.append("COMMAND --help' for more information.\n");
- usage.append("\n");
- err.write(usage.toString().getBytes("UTF-8"));
- err.flush();
- onExit(1);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/DispatchCommandProvider.java b/src/main/java/com/google/gerrit/server/ssh/DispatchCommandProvider.java
deleted file mode 100644
index 9372cabf15..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/DispatchCommandProvider.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.inject.Binding;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-import com.google.inject.TypeLiteral;
-
-import org.apache.sshd.server.CommandFactory.Command;
-
-import java.lang.annotation.Annotation;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Creates DispatchCommand using commands registered by {@link CommandModule}.
- */
-public class DispatchCommandProvider implements Provider<DispatchCommand> {
- @Inject
- private Injector injector;
-
- @Inject
- private DispatchCommand.Factory factory;
-
- private final String dispatcherName;
- private final CommandName parent;
-
- private volatile Map<String, Provider<Command>> map;
-
- public DispatchCommandProvider(final CommandName cn) {
- this(Commands.nameOf(cn), cn);
- }
-
- public DispatchCommandProvider(final String dispatcherName,
- final CommandName cn) {
- this.dispatcherName = dispatcherName;
- this.parent = cn;
- }
-
- @Override
- public DispatchCommand get() {
- return factory.create(dispatcherName, getMap());
- }
-
- private Map<String, Provider<Command>> getMap() {
- if (map == null) {
- synchronized (this) {
- if (map == null) {
- map = createMap();
- }
- }
- }
- return map;
- }
-
- @SuppressWarnings("unchecked")
- private Map<String, Provider<Command>> createMap() {
- final Map<String, Provider<Command>> m =
- new TreeMap<String, Provider<Command>>();
-
- for (final Binding<?> b : allCommands()) {
- final Annotation annotation = b.getKey().getAnnotation();
- if (annotation instanceof CommandName) {
- final CommandName n = (CommandName) annotation;
- if (!Commands.CMD_ROOT.equals(n) && Commands.isChild(parent, n)) {
- m.put(n.value(), (Provider<Command>) b.getProvider());
- }
- }
- }
-
- return Collections.unmodifiableMap(new LinkedHashMap(m));
- }
-
- private static final TypeLiteral<Command> type =
- new TypeLiteral<Command>() {};
-
- private List<Binding<Command>> allCommands() {
- return injector.findBindingsByType(type);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/HostKeyProvider.java b/src/main/java/com/google/gerrit/server/ssh/HostKeyProvider.java
deleted file mode 100644
index 420bd608ff..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/HostKeyProvider.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.server.config.SitePath;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
-import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-class HostKeyProvider implements Provider<KeyPairProvider> {
- private final File sitePath;
-
- @Inject
- HostKeyProvider(@SitePath final File sitePath) {
- this.sitePath = sitePath;
- }
-
- @Override
- public KeyPairProvider get() {
- final File anyKey = new File(sitePath, "ssh_host_key");
- final File rsaKey = new File(sitePath, "ssh_host_rsa_key");
- final File dsaKey = new File(sitePath, "ssh_host_dsa_key");
-
- final List<String> keys = new ArrayList<String>(2);
- if (rsaKey.exists()) {
- keys.add(rsaKey.getAbsolutePath());
- }
- if (dsaKey.exists()) {
- keys.add(dsaKey.getAbsolutePath());
- }
-
- if (anyKey.exists() && !keys.isEmpty()) {
- // If both formats of host key exist, we don't know which format
- // should be authoritative. Complain and abort.
- //
- keys.add(anyKey.getAbsolutePath());
- throw new ProvisionException("Multiple host keys exist: " + keys);
- }
-
- if (keys.isEmpty()) {
- // No administrator created host key? Generate and save our own.
- //
- final SimpleGeneratorHostKeyProvider keyp;
-
- keyp = new SimpleGeneratorHostKeyProvider();
- keyp.setPath(anyKey.getAbsolutePath());
- return keyp;
- }
-
- if (!SecurityUtils.isBouncyCastleRegistered()) {
- throw new ProvisionException("Bouncy Castle Crypto not installed;"
- + " needed to read server host keys: " + keys + "");
- }
- return new FileKeyPairProvider(keys.toArray(new String[keys.size()]));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/NoShell.java b/src/main/java/com/google/gerrit/server/ssh/NoShell.java
deleted file mode 100644
index b2bf8ce7b0..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/NoShell.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.server.http.SshServlet;
-
-import org.apache.sshd.server.ShellFactory;
-import org.eclipse.jgit.lib.Constants;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Dummy shell which prints a message and terminates.
- * <p>
- * This implementation is used by {@link SshServlet} to ensure clients who try
- * to SSH directly to this server without supplying a command will get a
- * reasonable error message, but cannot continue further.
- */
-class NoShell implements ShellFactory {
- public Shell createShell() {
- return new Shell() {
- private InputStream in;
- private OutputStream out;
- private OutputStream err;
- private ExitCallback exit;
-
- public void setInputStream(final InputStream in) {
- this.in = in;
- }
-
- public void setOutputStream(final OutputStream out) {
- this.out = out;
- }
-
- public void setErrorStream(final OutputStream err) {
- this.err = err;
- }
-
- public void setExitCallback(final ExitCallback callback) {
- this.exit = callback;
- }
-
- public void start(final Environment env) throws IOException {
- err.write(Constants.encodeASCII("gerrit: no shell available\n"));
- in.close();
- out.close();
- err.close();
- exit.onExit(127);
- }
-
- public void destroy() {
- }
- };
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshCurrentUserProvider.java b/src/main/java/com/google/gerrit/server/ssh/SshCurrentUserProvider.java
deleted file mode 100644
index 1a7a7ddebc..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshCurrentUserProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.AccessPath;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ssh.SshScopes.Context;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.ProvisionException;
-import com.google.inject.Singleton;
-
-@Singleton
-class SshCurrentUserProvider implements Provider<IdentifiedUser> {
- private final IdentifiedUser.RequestFactory factory;
-
- @Inject
- SshCurrentUserProvider(final IdentifiedUser.RequestFactory f) {
- factory = f;
- }
-
- @Override
- public IdentifiedUser get() {
- final Context ctx = SshScopes.getContext();
- final Account.Id id = ctx.session.getAttribute(SshUtil.CURRENT_ACCOUNT);
- if (id == null) {
- throw new ProvisionException("User not yet authenticated");
- }
- return factory.create(AccessPath.SSH, id);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshDaemon.java b/src/main/java/com/google/gerrit/server/ssh/SshDaemon.java
deleted file mode 100644
index 0e474d9899..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshDaemon.java
+++ /dev/null
@@ -1,515 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.Inject;
-import com.google.inject.Key;
-import com.google.inject.Singleton;
-
-import org.apache.mina.core.service.IoAcceptor;
-import org.apache.mina.core.session.IoSession;
-import org.apache.mina.transport.socket.SocketSessionConfig;
-import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
-import org.apache.sshd.SshServer;
-import org.apache.sshd.common.Cipher;
-import org.apache.sshd.common.Compression;
-import org.apache.sshd.common.KeyExchange;
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.Signature;
-import org.apache.sshd.common.cipher.AES128CBC;
-import org.apache.sshd.common.cipher.AES192CBC;
-import org.apache.sshd.common.cipher.AES256CBC;
-import org.apache.sshd.common.cipher.BlowfishCBC;
-import org.apache.sshd.common.cipher.CipherNone;
-import org.apache.sshd.common.cipher.TripleDESCBC;
-import org.apache.sshd.common.compression.CompressionNone;
-import org.apache.sshd.common.mac.HMACMD5;
-import org.apache.sshd.common.mac.HMACMD596;
-import org.apache.sshd.common.mac.HMACSHA1;
-import org.apache.sshd.common.mac.HMACSHA196;
-import org.apache.sshd.common.random.BouncyCastleRandom;
-import org.apache.sshd.common.random.JceRandom;
-import org.apache.sshd.common.random.SingletonRandomFactory;
-import org.apache.sshd.common.signature.SignatureDSA;
-import org.apache.sshd.common.signature.SignatureRSA;
-import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.ServerChannel;
-import org.apache.sshd.server.SessionFactory;
-import org.apache.sshd.server.UserAuth;
-import org.apache.sshd.server.CommandFactory.Command;
-import org.apache.sshd.server.auth.UserAuthPublicKey;
-import org.apache.sshd.server.channel.ChannelSession;
-import org.apache.sshd.server.kex.DHG1;
-import org.apache.sshd.server.kex.DHG14;
-import org.apache.sshd.server.session.ServerSession;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.lib.Config;
-
-import java.io.IOException;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * SSH daemon to communicate with Gerrit.
- * <p>
- * Use a Git URL such as <code>ssh://${email}@${host}:${port}/${path}</code>,
- * e.g. <code>ssh://sop@google.com@gerrit.com:8010/tools/gerrit.git</code> to
- * access the SSH daemon itself.
- * <p>
- * Versions of Git before 1.5.3 may require setting the username and port
- * properties in the user's <code>~/.ssh/config</code> file, and using a host
- * alias through a URL such as <code>gerrit-alias:/tools/gerrit.git:
- * <pre>
- * Host gerrit-alias
- * User sop@google.com
- * Hostname gerrit.com
- * Port 8010
- * </pre>
- */
-@Singleton
-public class SshDaemon extends SshServer implements SshInfo {
- private static final int DEFAULT_PORT = 29418;
-
- private static final Logger log = LoggerFactory.getLogger(SshDaemon.class);
-
- private static String format(final SocketAddress addr) {
- if (addr instanceof InetSocketAddress) {
- final InetSocketAddress inetAddr = (InetSocketAddress) addr;
- final InetAddress hostAddr = inetAddr.getAddress();
- String host;
- if (hostAddr.isAnyLocalAddress()) {
- host = "*";
- } else {
- host = "[" + hostAddr.getHostName() + "]";
- }
- return host + ":" + inetAddr.getPort();
- }
- return addr.toString();
- }
-
- private static boolean isIPv6(final InetAddress ip) {
- return ip instanceof Inet6Address
- && ip.getHostName().equals(ip.getHostAddress());
- }
-
- private final List<SocketAddress> listen;
- private final InetSocketAddress preferredAddress;
- private final boolean reuseAddress;
- private final boolean keepAlive;
- private final Collection<PublicKey> hostKeys;
- private volatile IoAcceptor acceptor;
-
- @Inject
- SshDaemon(final CommandFactory commandFactory,
- final PublickeyAuthenticator userAuth,
- final KeyPairProvider hostKeyProvider,
- @GerritServerConfig final Config cfg) {
- setPort(22/* never used */);
-
- listen = parseListen(cfg);
- reuseAddress = cfg.getBoolean("sshd", "reuseaddress", true);
- keepAlive = cfg.getBoolean("sshd", "tcpkeepalive", true);
-
- if (SecurityUtils.isBouncyCastleRegistered()) {
- initProviderBouncyCastle();
- } else {
- initProviderJce();
- }
- initCiphers(cfg);
- initMacs(cfg);
- initSignatures();
- initChannels();
- initCompression();
- initUserAuth(userAuth);
- setKeyPairProvider(hostKeyProvider);
- setCommandFactory(commandFactory);
- setShellFactory(new NoShell());
- setSessionFactory(new SessionFactory() {
- @Override
- protected ServerSession createSession(final IoSession io)
- throws Exception {
- if (io.getConfig() instanceof SocketSessionConfig) {
- final SocketSessionConfig c = (SocketSessionConfig) io.getConfig();
- c.setKeepAlive(keepAlive);
- }
-
- final ServerSession s = (ServerSession) super.createSession(io);
- s.setAttribute(SshUtil.REMOTE_PEER, io.getRemoteAddress());
- s.setAttribute(SshUtil.ACTIVE, new ArrayList<Command>(2));
- s.setAttribute(SshScopes.sessionMap, new HashMap<Key<?>, Object>());
- return s;
- }
- });
-
- hostKeys = computeHostKeys();
- preferredAddress = computePreferredAddress();
- }
-
- @Override
- public Collection<PublicKey> getHostKeys() {
- return hostKeys;
- }
-
- @Override
- public InetSocketAddress getAddress() {
- return preferredAddress;
- }
-
- public String getSshdAddress() {
- if (preferredAddress != null) {
- final InetAddress ip = preferredAddress.getAddress();
- String host;
- if (ip != null && ip.isAnyLocalAddress()) {
- host = "";
- } else if (isIPv6(ip)) {
- host = "[" + preferredAddress.getHostName() + "]";
- } else {
- host = preferredAddress.getHostName();
- }
- if (preferredAddress.getPort() != 22) {
- host += ":" + preferredAddress.getPort();
- }
- return host;
- }
- return null;
- }
-
- public IoAcceptor getIoAcceptor() {
- return acceptor;
- }
-
- @Override
- public synchronized void start() throws IOException {
- if (acceptor == null) {
- checkConfig();
- if (hostKeys.isEmpty()) {
- throw new IOException("No SSHD host key");
- }
-
- final NioSocketAcceptor ain = new NioSocketAcceptor();
- final SessionFactory handler = getSessionFactory();
- handler.setServer(this);
- ain.setHandler(handler);
- ain.setReuseAddress(reuseAddress);
- ain.bind(listen);
- acceptor = ain;
-
- log.info("Started Gerrit SSHD on " + addressList());
- }
- }
-
- @Override
- public synchronized void stop() {
- if (acceptor != null) {
- try {
- acceptor.dispose();
- log.info("Stopped Gerrit SSHD");
- } finally {
- acceptor = null;
- }
- }
- }
-
- private Collection<PublicKey> computeHostKeys() {
- final KeyPairProvider p = getKeyPairProvider();
- final List<PublicKey> keys = new ArrayList<PublicKey>(2);
- addPublicKey(keys, p, KeyPairProvider.SSH_DSS);
- addPublicKey(keys, p, KeyPairProvider.SSH_RSA);
- return Collections.unmodifiableList(keys);
- }
-
- private static void addPublicKey(final Collection<PublicKey> out,
- final KeyPairProvider p, final String type) {
- final KeyPair pair = p.loadKey(type);
- if (pair != null && pair.getPublic() != null) {
- out.add(pair.getPublic());
- }
- }
-
- private InetSocketAddress computePreferredAddress() {
- for (final SocketAddress addr : listen) {
- if (!(addr instanceof InetSocketAddress)) {
- continue;
- }
-
- InetSocketAddress inetAddr = (InetSocketAddress) addr;
- if (inetAddr.getAddress().isLoopbackAddress()) {
- continue;
- }
- return inetAddr;
- }
-
- // No non-loopback address available? Try any address then.
- //
- for (final SocketAddress addr : listen) {
- if (addr instanceof InetSocketAddress) {
- return (InetSocketAddress) addr;
- }
- }
-
- // We give up, with no valid address.
- //
- return null;
- }
-
- private String addressList() {
- final StringBuilder r = new StringBuilder();
- for (Iterator<SocketAddress> i = listen.iterator(); i.hasNext();) {
- r.append(format(i.next()));
- if (i.hasNext()) {
- r.append(", ");
- }
- }
- return r.toString();
- }
-
- private List<SocketAddress> parseListen(final Config cfg) {
- final ArrayList<SocketAddress> bind = new ArrayList<SocketAddress>(2);
- final String[] want = cfg.getStringList("sshd", null, "listenaddress");
- if (want == null || want.length == 0) {
- bind.add(new InetSocketAddress(DEFAULT_PORT));
- return bind;
- }
-
- for (final String desc : want) {
- try {
- bind.add(toSocketAddress(desc));
- } catch (IllegalArgumentException e) {
- log.error("Bad sshd.listenaddress: " + desc + ": " + e.getMessage());
- }
- }
-
- return bind;
- }
-
- private SocketAddress toSocketAddress(final String desc) {
- String hostStr;
- String portStr;
-
- if (desc.startsWith("[")) {
- // IPv6, as a raw IP address.
- //
- final int hostEnd = desc.indexOf(']');
- if (hostEnd < 0) {
- throw new IllegalArgumentException("invalid IPv6 representation");
- }
-
- hostStr = desc.substring(1, hostEnd);
- portStr = desc.substring(hostEnd + 1);
- } else {
- // IPv4, or a host name.
- //
- final int hostEnd = desc.indexOf(':');
- hostStr = 0 <= hostEnd ? desc.substring(0, hostEnd) : desc;
- portStr = 0 <= hostEnd ? desc.substring(hostEnd) : "";
- }
-
- if ("*".equals(hostStr)) {
- hostStr = "";
- }
- if (portStr.startsWith(":")) {
- portStr = portStr.substring(1);
- }
-
- final int port;
- if (portStr.length() > 0) {
- try {
- port = Integer.parseInt(portStr);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("invalid port");
- }
- } else {
- port = DEFAULT_PORT;
- }
-
- if (hostStr.length() > 0) {
- try {
- return new InetSocketAddress(InetAddress.getByName(hostStr), port);
- } catch (UnknownHostException e) {
- throw new IllegalArgumentException(e.getMessage(), e);
- }
- } else {
- return new InetSocketAddress(port);
- }
- }
-
- @SuppressWarnings("unchecked")
- private void initProviderBouncyCastle() {
- setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>> asList(
- new DHG14.Factory(), new DHG1.Factory()));
- setRandomFactory(new SingletonRandomFactory(
- new BouncyCastleRandom.Factory()));
- }
-
- @SuppressWarnings("unchecked")
- private void initProviderJce() {
- setKeyExchangeFactories(Arrays
- .<NamedFactory<KeyExchange>> asList(new DHG1.Factory()));
- setRandomFactory(new SingletonRandomFactory(new JceRandom.Factory()));
- }
-
- @SuppressWarnings("unchecked")
- private void initCiphers(final Config cfg) {
- final List<NamedFactory<Cipher>> a = new LinkedList<NamedFactory<Cipher>>();
- a.add(new AES128CBC.Factory());
- a.add(new TripleDESCBC.Factory());
- a.add(new BlowfishCBC.Factory());
- a.add(new AES192CBC.Factory());
- a.add(new AES256CBC.Factory());
-
- for (Iterator<NamedFactory<Cipher>> i = a.iterator(); i.hasNext();) {
- final NamedFactory<Cipher> f = i.next();
- try {
- final Cipher c = f.create();
- final byte[] key = new byte[c.getBlockSize()];
- final byte[] iv = new byte[c.getIVSize()];
- c.init(Cipher.Mode.Encrypt, key, iv);
- } catch (InvalidKeyException e) {
- log.warn("Disabling cipher " + f.getName() + ": " + e.getMessage()
- + "; try installing unlimited cryptography extension");
- i.remove();
- } catch (Exception e) {
- log.warn("Disabling cipher " + f.getName() + ": " + e.getMessage());
- i.remove();
- }
- }
-
- a.add(null);
- a.add(new CipherNone.Factory());
- setCipherFactories(filter(cfg, "cipher", a.toArray(new NamedFactory[a
- .size()])));
- }
-
- @SuppressWarnings("unchecked")
- private void initMacs(final Config cfg) {
- setMacFactories(filter(cfg, "mac", new HMACMD5.Factory(),
- new HMACSHA1.Factory(), new HMACMD596.Factory(),
- new HMACSHA196.Factory()));
- }
-
- private static <T> List<NamedFactory<T>> filter(final Config cfg,
- final String key, final NamedFactory<T>... avail) {
- final ArrayList<NamedFactory<T>> def = new ArrayList<NamedFactory<T>>();
- for (final NamedFactory<T> n : avail) {
- if (n == null) {
- break;
- }
- def.add(n);
- }
-
- final String[] want = cfg.getStringList("sshd", null, key);
- if (want == null || want.length == 0) {
- return def;
- }
-
- boolean didClear = false;
- for (final String setting : want) {
- String name = setting.trim();
- boolean add = true;
- if (name.startsWith("-")) {
- add = false;
- name = name.substring(1).trim();
- } else if (name.startsWith("+")) {
- name = name.substring(1).trim();
- } else if (!didClear) {
- didClear = true;
- def.clear();
- }
-
- final NamedFactory<T> n = find(name, avail);
- if (n == null) {
- final StringBuilder msg = new StringBuilder();
- msg.append("sshd." + key + " = " + name + " unsupported; only ");
- for (int i = 0; i < avail.length; i++) {
- if (avail[i] == null) {
- continue;
- }
- if (i > 0) {
- msg.append(", ");
- }
- msg.append(avail[i].getName());
- }
- msg.append(" is supported");
- log.error(msg.toString());
- } else if (add) {
- if (!def.contains(n)) {
- def.add(n);
- }
- } else {
- def.remove(n);
- }
- }
-
- return def;
- }
-
- private static <T> NamedFactory<T> find(final String name,
- final NamedFactory<T>... avail) {
- for (final NamedFactory<T> n : avail) {
- if (n != null && name.equals(n.getName())) {
- return n;
- }
- }
- return null;
- }
-
- @SuppressWarnings("unchecked")
- private void initSignatures() {
- setSignatureFactories(Arrays.<NamedFactory<Signature>> asList(
- new SignatureDSA.Factory(), new SignatureRSA.Factory()));
- }
-
- @SuppressWarnings("unchecked")
- private void initCompression() {
- // Always disable transparent compression. The majority of our data
- // transfer is highly compressed Git pack files. We cannot make them
- // any smaller than they already are.
- //
- setCompressionFactories(Arrays
- .<NamedFactory<Compression>> asList(new CompressionNone.Factory()));
- }
-
- @SuppressWarnings("unchecked")
- private void initChannels() {
- setChannelFactories(Arrays
- .<NamedFactory<ServerChannel>> asList(new ChannelSession.Factory()));
- }
-
- @SuppressWarnings("unchecked")
- private void initUserAuth(final PublickeyAuthenticator pubkey) {
- setUserAuthFactories(Arrays
- .<NamedFactory<UserAuth>> asList(new UserAuthPublicKey.Factory()));
- setPublickeyAuthenticator(pubkey);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshInfo.java b/src/main/java/com/google/gerrit/server/ssh/SshInfo.java
deleted file mode 100644
index 37e15843d6..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import java.net.InetSocketAddress;
-import java.security.PublicKey;
-import java.util.Collection;
-
-public interface SshInfo {
- InetSocketAddress getAddress();
-
- Collection<PublicKey> getHostKeys();
-
- String getSshdAddress();
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java b/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java
deleted file mode 100644
index ec33fb4374..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshKeyCache.java
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-/** Provides the {@link SshKeyCacheEntry}. */
-public interface SshKeyCache {
- public Iterable<SshKeyCacheEntry> get(String username);
-
- public void evict(String username);
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheEntry.java b/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheEntry.java
deleted file mode 100644
index 2daf1847c6..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheEntry.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.security.PublicKey;
-import java.util.Collections;
-
-class SshKeyCacheEntry {
- private static final Logger log =
- LoggerFactory.getLogger(SshKeyCacheEntry.class);
-
- private final AccountSshKey.Id id;
- private final PublicKey publicKey;
-
- SshKeyCacheEntry(final AccountSshKey.Id i, final PublicKey k) {
- id = i;
- publicKey = k;
- }
-
- Account.Id getAccount() {
- return id.getParentKey();
- }
-
- boolean match(final PublicKey inkey) {
- return publicKey.equals(inkey);
- }
-
- void updateLastUsed(final SchemaFactory<ReviewDb> schema) {
- try {
- final ReviewDb db = schema.open();
- try {
- final AccountSshKey k = db.accountSshKeys().get(id);
- if (k != null) {
- k.setLastUsedOn();
- db.accountSshKeys().update(Collections.singleton(k));
- }
- } finally {
- db.close();
- }
- } catch (OrmException e) {
- log.warn("Failed to update \"" + id + "\" SSH key used", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheImpl.java b/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheImpl.java
deleted file mode 100644
index c22b231ed7..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshKeyCacheImpl.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.cache.Cache;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.cache.SelfPopulatingCache;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Provides the {@link SshKeyCacheEntry}. */
-@Singleton
-public class SshKeyCacheImpl implements SshKeyCache {
- private static final Logger log = LoggerFactory.getLogger(SshKeyCacheImpl.class);
- private static final String CACHE_NAME = "sshkeys";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- final TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>> type =
- new TypeLiteral<Cache<String, Iterable<SshKeyCacheEntry>>>() {};
- core(type, CACHE_NAME);
- bind(SshKeyCacheImpl.class);
- bind(SshKeyCache.class).to(SshKeyCacheImpl.class);
- }
- };
- }
-
- private final SchemaFactory<ReviewDb> schema;
- private final SelfPopulatingCache<String, Iterable<SshKeyCacheEntry>> self;
-
- @Inject
- SshKeyCacheImpl(final SchemaFactory<ReviewDb> schema,
- @Named(CACHE_NAME) final Cache<String, Iterable<SshKeyCacheEntry>> raw) {
- this.schema = schema;
- self = new SelfPopulatingCache<String, Iterable<SshKeyCacheEntry>>(raw) {
- @Override
- protected Iterable<SshKeyCacheEntry> createEntry(final String username)
- throws Exception {
- return lookup(username);
- }
-
- @Override
- protected Iterable<SshKeyCacheEntry> missing(final String username) {
- return Collections.emptyList();
- }
- };
- }
-
- public Iterable<SshKeyCacheEntry> get(String username) {
- return self.get(username);
- }
-
- public void evict(String username) {
- self.remove(username);
- }
-
- private Iterable<SshKeyCacheEntry> lookup(final String username)
- throws Exception {
- final ReviewDb db = schema.open();
- try {
- final Account user = db.accounts().bySshUserName(username);
- if (user == null) {
- return Collections.<SshKeyCacheEntry> emptyList();
- }
-
- final List<SshKeyCacheEntry> kl = new ArrayList<SshKeyCacheEntry>(4);
- for (final AccountSshKey k : db.accountSshKeys().valid(user.getId())) {
- add(db, kl, k);
- }
- if (kl.isEmpty()) {
- return Collections.<SshKeyCacheEntry> emptyList();
- }
- return Collections.unmodifiableList(kl);
- } finally {
- db.close();
- }
- }
-
- private void add(ReviewDb db, List<SshKeyCacheEntry> kl, AccountSshKey k) {
- try {
- kl.add(new SshKeyCacheEntry(k.getKey(), SshUtil.parse(k)));
- } catch (OutOfMemoryError e) {
- // This is the only case where we assume the problem has nothing
- // to do with the key object, and instead we must abort this load.
- //
- throw e;
- } catch (Throwable e) {
- markInvalid(db, k);
- }
- }
-
- private void markInvalid(final ReviewDb db, final AccountSshKey k) {
- try {
- log.info("Flagging SSH key " + k.getKey() + " invalid");
- k.setInvalid();
- db.accountSshKeys().update(Collections.singleton(k));
- } catch (OrmException e) {
- log.error("Failed to mark SSH key" + k.getKey() + " invalid", e);
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshModule.java b/src/main/java/com/google/gerrit/server/ssh/SshModule.java
deleted file mode 100644
index e04486f451..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshModule.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import static com.google.inject.Scopes.SINGLETON;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.pgm.CmdLineParser;
-import com.google.gerrit.pgm.OptionHandlerFactory;
-import com.google.gerrit.pgm.OptionHandlerUtil;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.RemotePeer;
-import com.google.gerrit.server.config.FactoryModule;
-import com.google.gerrit.server.config.GerritRequestModule;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.ssh.args4j.AccountGroupIdHandler;
-import com.google.gerrit.server.ssh.args4j.AccountIdHandler;
-import com.google.gerrit.server.ssh.args4j.PatchSetIdHandler;
-import com.google.gerrit.server.ssh.args4j.ProjectControlHandler;
-import com.google.gerrit.server.ssh.commands.DefaultCommandModule;
-import com.google.inject.Key;
-import com.google.inject.Provider;
-import com.google.inject.TypeLiteral;
-import com.google.inject.assistedinject.FactoryProvider;
-import com.google.inject.servlet.RequestScoped;
-import com.google.inject.servlet.SessionScoped;
-
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.session.AbstractSession;
-import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.session.ServerSession;
-import org.kohsuke.args4j.spi.OptionHandler;
-
-import java.net.SocketAddress;
-
-/** Configures standard dependencies for {@link SshDaemon}. */
-public class SshModule extends FactoryModule {
- private static final String NAME = "Gerrit Code Review";
-
- @Override
- protected void configure() {
- bindScope(SessionScoped.class, SshScopes.SESSION);
- bindScope(RequestScoped.class, SshScopes.REQUEST);
-
- configureSessionScope();
- configureRequestScope();
- configureCmdLineParser();
-
- bind(SshInfo.class).to(SshDaemon.class).in(SINGLETON);
- factory(DispatchCommand.Factory.class);
-
- bind(DispatchCommandProvider.class).annotatedWith(Commands.CMD_ROOT)
- .toInstance(new DispatchCommandProvider(NAME, Commands.CMD_ROOT));
- bind(CommandFactoryProvider.class);
- bind(CommandFactory.class).toProvider(CommandFactoryProvider.class);
-
- bind(PublickeyAuthenticator.class).to(DatabasePubKeyAuth.class);
- bind(KeyPairProvider.class).toProvider(HostKeyProvider.class).in(SINGLETON);
-
- install(new DefaultCommandModule());
- }
-
- private void configureSessionScope() {
- bind(ServerSession.class).toProvider(new Provider<ServerSession>() {
- @Override
- public ServerSession get() {
- return SshScopes.getContext().session;
- }
- }).in(SshScopes.SESSION);
- bind(AbstractSession.class).to(ServerSession.class).in(SshScopes.SESSION);
-
- bind(SocketAddress.class).annotatedWith(RemotePeer.class).toProvider(
- new Provider<SocketAddress>() {
- @Override
- public SocketAddress get() {
- return SshScopes.getContext().session
- .getAttribute(SshUtil.REMOTE_PEER);
- }
- }).in(SshScopes.SESSION);
- }
-
- private void configureRequestScope() {
- install(new GerritRequestModule());
- bind(IdentifiedUser.class).toProvider(SshCurrentUserProvider.class).in(
- SshScopes.REQUEST);
- bind(CurrentUser.class).to(IdentifiedUser.class);
- }
-
- private void configureCmdLineParser() {
- factory(CmdLineParser.Factory.class);
-
- registerOptionHandler(Account.Id.class, AccountIdHandler.class);
- registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class);
- registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
- registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
- }
-
- private <T> void registerOptionHandler(Class<T> type,
- Class<? extends OptionHandler<T>> impl) {
- final Key<OptionHandlerFactory<T>> key = OptionHandlerUtil.keyFor(type);
-
- final TypeLiteral<OptionHandlerFactory<T>> factoryType =
- new TypeLiteral<OptionHandlerFactory<T>>() {};
-
- final TypeLiteral<? extends OptionHandler<T>> implType =
- TypeLiteral.get(impl);
-
- bind(key).toProvider(FactoryProvider.newFactory(factoryType, implType));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshScopes.java b/src/main/java/com/google/gerrit/server/ssh/SshScopes.java
deleted file mode 100644
index 18b1e5cf24..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshScopes.java
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.inject.Key;
-import com.google.inject.OutOfScopeException;
-import com.google.inject.Provider;
-import com.google.inject.Scope;
-
-import org.apache.sshd.common.session.AttributeKey;
-import org.apache.sshd.server.session.ServerSession;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** Guice scopes for state during an SSH connection. */
-class SshScopes {
- static class Context {
- final ServerSession session;
- final Map<Key<?>, Object> map;
-
- Context(final ServerSession s) {
- session = s;
- map = new HashMap<Key<?>, Object>();
- }
- }
-
- static final AttributeKey<Map<Key<?>, Object>> sessionMap =
- new AttributeKey<Map<Key<?>, Object>>();
-
- static final ThreadLocal<Context> current = new ThreadLocal<Context>();
-
- static Context getContext() {
- final Context ctx = current.get();
- if (ctx == null) {
- throw new OutOfScopeException(
- "Cannot access scoped object; not in request/command.");
- }
- return ctx;
- }
-
- /** Returns exactly one instance for the scope of the SSH connection. */
- static final Scope SESSION = new Scope() {
- public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
- return new Provider<T>() {
- public T get() {
- final Context ctx = getContext();
- final Map<Key<?>, Object> map = ctx.session.getAttribute(sessionMap);
- synchronized (map) {
- @SuppressWarnings("unchecked")
- T t = (T) map.get(key);
- if (t == null) {
- t = creator.get();
- map.put(key, t);
- }
- return t;
- }
- }
-
- @Override
- public String toString() {
- return String.format("%s[%s]", creator, SESSION);
- }
- };
- }
-
- @Override
- public String toString() {
- return "SshScopes.SESSION";
- }
- };
-
- /** Returns exactly one instance per command executed. */
- static final Scope REQUEST = new Scope() {
- public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
- return new Provider<T>() {
- public T get() {
- final Map<Key<?>, Object> map = getContext().map;
- synchronized (map) {
- @SuppressWarnings("unchecked")
- T t = (T) map.get(key);
- if (t == null) {
- t = creator.get();
- map.put(key, t);
- }
- return t;
- }
- }
-
- @Override
- public String toString() {
- return String.format("%s[%s]", creator, REQUEST);
- }
- };
- }
-
- @Override
- public String toString() {
- return "SshScopes.REQUEST";
- }
- };
-
- private SshScopes() {
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/SshUtil.java b/src/main/java/com/google/gerrit/server/ssh/SshUtil.java
deleted file mode 100644
index 78bea6caca..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/SshUtil.java
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountSshKey;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.session.AttributeKey;
-import org.apache.sshd.common.util.Buffer;
-import org.apache.sshd.server.CommandFactory.Command;
-import org.eclipse.jgit.lib.Constants;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.SocketAddress;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PublicKey;
-import java.security.interfaces.DSAPublicKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.util.List;
-
-/** Utilities to support SSH operations. */
-public class SshUtil {
- /** Server session attribute holding the {@link Account.Id}. */
- public static final AttributeKey<Account.Id> CURRENT_ACCOUNT =
- new AttributeKey<Account.Id>();
-
- /** Server session attribute holding the remote {@link SocketAddress}. */
- public static final AttributeKey<SocketAddress> REMOTE_PEER =
- new AttributeKey<SocketAddress>();
-
- /** Server session attribute holding the current commands. */
- public static final AttributeKey<List<Command>> ACTIVE =
- new AttributeKey<List<Command>>();
-
- /**
- * Parse a public key into its Java type.
- *
- * @param key the account key to parse.
- * @return the valid public key object.
- * @throws InvalidKeySpecException the key supplied is not a valid SSH key.
- * @throws NoSuchAlgorithmException the JVM is missing the key algorithm.
- * @throws NoSuchProviderException the JVM is missing the provider.
- */
- public static PublicKey parse(final AccountSshKey key)
- throws NoSuchAlgorithmException, InvalidKeySpecException,
- NoSuchProviderException {
- try {
- final String s = key.getEncodedKey();
- if (s == null) {
- throw new InvalidKeySpecException("No key string");
- }
- final byte[] bin = Base64.decodeBase64(Constants.encodeASCII(s));
- return new Buffer(bin).getPublicKey();
- } catch (RuntimeException re) {
- throw new InvalidKeySpecException("Cannot parse key", re);
- }
- }
-
- /**
- * Convert an RFC 4716 style key to an OpenSSH style key.
- *
- * @param keyStr the key string to convert.
- * @return <code>keyStr</code> if conversion failed; otherwise the converted
- * key, in OpenSSH key format.
- */
- public static String toOpenSshPublicKey(final String keyStr) {
- try {
- final StringBuilder strBuf = new StringBuilder();
- final BufferedReader br = new BufferedReader(new StringReader(keyStr));
- String line = br.readLine(); // BEGIN SSH2 line...
- if (!line.equals("---- BEGIN SSH2 PUBLIC KEY ----")) {
- return keyStr;
- }
-
- while ((line = br.readLine()) != null) {
- if (line.indexOf(':') == -1) {
- strBuf.append(line);
- break;
- }
- }
-
- while ((line = br.readLine()) != null) {
- if (line.startsWith("---- ")) {
- break;
- }
- strBuf.append(line);
- }
-
- final PublicKey key =
- new Buffer(Base64.decodeBase64(Constants.encodeASCII(strBuf
- .toString()))).getPublicKey();
- if (key instanceof RSAPublicKey) {
- strBuf.insert(0, KeyPairProvider.SSH_RSA + " ");
-
- } else if (key instanceof DSAPublicKey) {
- strBuf.insert(0, KeyPairProvider.SSH_DSS + " ");
-
- } else {
- return keyStr;
- }
-
- strBuf.append(' ');
- strBuf.append("converted-key");
- return strBuf.toString();
- } catch (IOException e) {
- return keyStr;
- } catch (RuntimeException re) {
- return keyStr;
- } catch (NoSuchAlgorithmException e) {
- return keyStr;
- } catch (InvalidKeySpecException e) {
- return keyStr;
- } catch (NoSuchProviderException e) {
- return keyStr;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/args4j/AccountGroupIdHandler.java b/src/main/java/com/google/gerrit/server/ssh/args4j/AccountGroupIdHandler.java
deleted file mode 100644
index 63be4a5bd0..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/args4j/AccountGroupIdHandler.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.args4j;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
-import org.kohsuke.args4j.spi.Setter;
-
-public class AccountGroupIdHandler extends OptionHandler<AccountGroup.Id> {
- private final GroupCache groupCache;
-
- @SuppressWarnings("unchecked")
- @Inject
- public AccountGroupIdHandler(final GroupCache groupCache,
- @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
- @Assisted final Setter setter) {
- super(parser, option, setter);
- this.groupCache = groupCache;
- }
-
- @Override
- public final int parseArguments(final Parameters params)
- throws CmdLineException {
- final String n = params.getParameter(0);
- final AccountGroup group = groupCache.lookup(n);
- if (group == null) {
- throw new CmdLineException(owner, "Group \"" + n + "\" does not exist");
- }
- setter.addValue(group.getId());
- return 1;
- }
-
- @Override
- public final String getDefaultMetaVariable() {
- return "GROUP";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/args4j/AccountIdHandler.java b/src/main/java/com/google/gerrit/server/ssh/args4j/AccountIdHandler.java
deleted file mode 100644
index 926d93435c..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/args4j/AccountIdHandler.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.args4j;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
-import org.kohsuke.args4j.spi.Setter;
-
-public class AccountIdHandler extends OptionHandler<Account.Id> {
- private final AccountResolver accountResolver;
-
- @SuppressWarnings("unchecked")
- @Inject
- public AccountIdHandler(final AccountResolver accountResolver,
- @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
- @Assisted final Setter setter) {
- super(parser, option, setter);
- this.accountResolver = accountResolver;
- }
-
- @Override
- public final int parseArguments(final Parameters params)
- throws CmdLineException {
- final String token = params.getParameter(0);
- final Account.Id accountId;
- try {
- final Account a = accountResolver.find(token);
- if (a == null) {
- throw new CmdLineException(owner, "\"" + token + "\" is not registered");
- }
- accountId = a.getId();
- } catch (OrmException e) {
- throw new CmdLineException(owner, "database is down");
- }
- setter.addValue(accountId);
- return 1;
- }
-
- @Override
- public final String getDefaultMetaVariable() {
- return "EMAIL";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/args4j/PatchSetIdHandler.java b/src/main/java/com/google/gerrit/server/ssh/args4j/PatchSetIdHandler.java
deleted file mode 100644
index 32ede0ecc8..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/args4j/PatchSetIdHandler.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.args4j;
-
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
-import org.kohsuke.args4j.spi.Setter;
-
-public class PatchSetIdHandler extends OptionHandler<PatchSet.Id> {
- @SuppressWarnings("unchecked")
- @Inject
- public PatchSetIdHandler(@Assisted final CmdLineParser parser,
- @Assisted final OptionDef option, @Assisted final Setter setter) {
- super(parser, option, setter);
- }
-
- @Override
- public final int parseArguments(final Parameters params)
- throws CmdLineException {
- final String token = params.getParameter(0);
- final PatchSet.Id id;
- try {
- id = PatchSet.Id.parse(token);
- } catch (IllegalArgumentException e) {
- throw new CmdLineException(owner, "\"" + token
- + "\" is not a valid patch set");
- }
-
- setter.addValue(id);
- return 1;
- }
-
- @Override
- public final String getDefaultMetaVariable() {
- return "CHANGE,PATCHSET";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/args4j/ProjectControlHandler.java b/src/main/java/com/google/gerrit/server/ssh/args4j/ProjectControlHandler.java
deleted file mode 100644
index 72e9e06ed7..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/args4j/ProjectControlHandler.java
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.args4j;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Parameters;
-import org.kohsuke.args4j.spi.Setter;
-
-public class ProjectControlHandler extends OptionHandler<ProjectControl> {
- private final ProjectControl.Factory projectControlFactory;
-
- @SuppressWarnings("unchecked")
- @Inject
- public ProjectControlHandler(
- final ProjectControl.Factory projectControlFactory,
- @Assisted final CmdLineParser parser, @Assisted final OptionDef option,
- @Assisted final Setter setter) {
- super(parser, option, setter);
- this.projectControlFactory = projectControlFactory;
- }
-
- @Override
- public final int parseArguments(final Parameters params)
- throws CmdLineException {
- final String token = params.getParameter(0);
- String projectName = token;
-
- if (projectName.endsWith(".git")) {
- // Be nice and drop the trailing ".git" suffix, which we never keep
- // in our database, but clients might mistakenly provide anyway.
- //
- projectName = projectName.substring(0, projectName.length() - 4);
- }
-
- if (projectName.startsWith("/")) {
- // Be nice and drop the leading "/" if supplied by an absolute path.
- // We don't have a file system hierarchy, just a flat namespace in
- // the database's Project entities. We never encode these with a
- // leading '/' but users might accidentally include them in Git URLs.
- //
- projectName = projectName.substring(1);
- }
-
- final ProjectControl control;
- try {
- control =
- projectControlFactory.validateFor(new Project.NameKey(projectName));
- } catch (NoSuchProjectException e) {
- throw new CmdLineException(owner, "'" + token + "': not a Gerrit project");
- }
-
- setter.addValue(control);
- return 1;
- }
-
- @Override
- public final String getDefaultMetaVariable() {
- return "PROJECT";
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AbstractGitCommand.java b/src/main/java/com/google/gerrit/server/ssh/commands/AbstractGitCommand.java
deleted file mode 100644
index 9a02e940d9..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AbstractGitCommand.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.inject.Inject;
-
-import org.kohsuke.args4j.Argument;
-import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.IOException;
-
-abstract class AbstractGitCommand extends BaseCommand {
- @Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
- protected ProjectControl projectControl;
-
- @Inject
- protected GitRepositoryManager repoManager;
-
- protected Repository repo;
- protected Project project;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- AbstractGitCommand.this.service();
- }
- });
- }
-
- private void service() throws IOException, Failure {
- project = projectControl.getProjectState().getProject();
-
- final String name = project.getName();
- try {
- repo = repoManager.openRepository(name);
- } catch (RepositoryNotFoundException e) {
- throw new Failure(1, "fatal: '" + name + "': not a git archive", e);
- }
-
- try {
- runImpl();
- } finally {
- repo.close();
- }
- }
-
- protected abstract void runImpl() throws IOException, Failure;
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminCreateProject.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminCreateProject.java
deleted file mode 100644
index 1c76b05af3..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminCreateProject.java
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.Project.SubmitType;
-import com.google.gerrit.git.GitRepositoryManager;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.server.config.AuthConfig;
-import com.google.gerrit.server.ssh.AdminCommand;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.Repository;
-import org.kohsuke.args4j.Option;
-
-import java.io.PrintWriter;
-import java.util.Collections;
-
-/** Create a new project. **/
-@AdminCommand
-final class AdminCreateProject extends BaseCommand {
- @Option(name = "--name", required = true, aliases = {"-n"}, metaVar = "NAME", usage = "name of project to be created")
- private String projectName;
-
- @Option(name = "--owner", aliases = {"-o"}, usage = "owner of project\n"
- + "(default: Administrators)")
- private AccountGroup.Id ownerId;
-
- @Option(name = "--description", aliases = {"-d"}, metaVar = "DESC", usage = "description of project")
- private String projectDescription = "";
-
- @Option(name = "--submit-type", aliases = {"-t"}, usage = "project submit type\n"
- + "(default: MERGE_IF_NECESSARY)")
- private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY;
-
- @Option(name = "--use-contributor-agreements", aliases = {"--ca"}, usage = "if contributor agreement is required")
- private boolean contributorAgreements;
-
- @Option(name = "--use-signed-off-by", aliases = {"--so"}, usage = "if signed-off-by is required")
- private boolean signedOffBy;
-
- @Option(name = "--branch", aliases = {"-b"}, metaVar = "BRANCH", usage = "initial branch name\n"
- + "(default: master)")
- private String branch = Constants.MASTER;
-
- @Inject
- private ReviewDb db;
-
- @Inject
- private GitRepositoryManager repoManager;
-
- @Inject
- private AuthConfig authConfig;
-
- @Inject
- private ReplicationQueue rq;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- PrintWriter p = toPrintWriter(out);
-
- ownerId = authConfig.getAdministratorsGroup();
- parseCommandLine();
-
- try {
- validateParameters();
-
- Transaction txn = db.beginTransaction();
-
- createProject(txn);
-
- Repository repo = repoManager.createRepository(projectName);
- repo.create(true);
- repo.writeSymref(Constants.HEAD, branch);
- repoManager.setProjectDescription(projectName, projectDescription);
-
- txn.commit();
-
- rq.replicateNewProject(new Project.NameKey(projectName), branch);
- } catch (Exception e) {
- p.print("Error when trying to create project: " + e.getMessage()
- + "\n");
- p.flush();
- }
-
- }
- });
- }
-
- private void createProject(Transaction txn) throws OrmException {
- final Project.NameKey newProjectNameKey = new Project.NameKey(projectName);
-
- final Project newProject =
- new Project(newProjectNameKey, new Project.Id(db.nextProjectId()));
-
- newProject.setDescription(projectDescription);
- newProject.setSubmitType(submitType);
- newProject.setUseContributorAgreements(contributorAgreements);
- newProject.setUseSignedOffBy(signedOffBy);
-
- db.projects().insert(Collections.singleton(newProject), txn);
-
- final ProjectRight.Key prk =
- new ProjectRight.Key(newProjectNameKey, ApprovalCategory.OWN, ownerId);
- final ProjectRight pr = new ProjectRight(prk);
- pr.setMaxValue((short) 1);
- pr.setMinValue((short) 1);
- db.projectRights().insert(Collections.singleton(pr), txn);
- }
-
- private void validateParameters() throws Failure {
- if (projectName.endsWith(".git")) {
- projectName =
- projectName.substring(0, projectName.length() - ".git".length());
- }
-
- while (branch.startsWith("/")) {
- branch = branch.substring(1);
- }
- if (!branch.startsWith(Constants.R_HEADS)) {
- branch = Constants.R_HEADS + branch;
- }
- if (!Repository.isValidRefName(branch)) {
- throw new Failure(1, "--branch \"" + branch + "\" is not a valid name");
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminFlushCaches.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminFlushCaches.java
deleted file mode 100644
index aba48bd303..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminFlushCaches.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.AdminCommand;
-
-import net.sf.ehcache.Ehcache;
-
-import org.kohsuke.args4j.Option;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedSet;
-
-/** Causes the caches to purge all entries and reload. */
-@AdminCommand
-final class AdminFlushCaches extends CacheCommand {
- @Option(name = "--cache", usage = "flush named cache", metaVar = "NAME")
- private List<String> caches = new ArrayList<String>();
-
- @Option(name = "--all", usage = "flush all caches")
- private boolean all;
-
- @Option(name = "--list", usage = "list available caches")
- private boolean list;
-
- private PrintWriter p;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- flush();
- }
- });
- }
-
- private void flush() throws Failure {
- p = toPrintWriter(err);
- if (list) {
- if (all || caches.size() > 0) {
- throw error("error: cannot use --list with --all or --cache");
- }
- doList();
- return;
- }
-
- if (all && caches.size() > 0) {
- throw error("error: cannot combine --all and --cache");
- } else if (!all && caches.size() == 1 && caches.contains("all")) {
- caches.clear();
- all = true;
- } else if (!all && caches.isEmpty()) {
- all = true;
- }
-
- final SortedSet<String> names = cacheNames();
- for (final String n : caches) {
- if (!names.contains(n)) {
- throw error("error: cache \"" + n + "\" not recognized");
- }
- }
- doBulkFlush();
- }
-
- private static UnloggedFailure error(final String msg) {
- return new UnloggedFailure(1, msg);
- }
-
- private void doList() {
- for (final String name : cacheNames()) {
- p.print(name);
- p.print('\n');
- }
- p.flush();
- }
-
- private void doBulkFlush() {
- try {
- for (final Ehcache c : getAllCaches()) {
- final String name = c.getName();
- if (flush(name)) {
- try {
- c.removeAll();
- } catch (Throwable e) {
- p.println("error: cannot flush cache \"" + name + "\": " + e);
- }
- }
- }
- } finally {
- p.flush();
- }
- }
-
- private boolean flush(final String cacheName) {
- if (caches.contains(cacheName)) {
- return true;
-
- } else if (all) {
- if ("web_sessions".equals(cacheName)) {
- return false;
- }
- return true;
-
- } else {
- return false;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminReplicate.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminReplicate.java
deleted file mode 100644
index efb66e6cdf..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminReplicate.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.git.PushAllProjectsOp;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.ssh.AdminCommand;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.inject.Inject;
-
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.Option;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Force a project to replicate, again. */
-@AdminCommand
-final class AdminReplicate extends BaseCommand {
- @Option(name = "--all", usage = "push all known projects")
- private boolean all;
-
- @Option(name = "--url", metaVar = "PATTERN", usage = "pattern to match URL on")
- private String urlMatch;
-
- @Argument(index = 0, multiValued = true, metaVar = "PROJECT", usage = "project name")
- private List<String> projectNames = new ArrayList<String>(2);
-
- @Inject
- private PushAllProjectsOp.Factory pushAllOpFactory;
-
- @Inject
- private ReplicationQueue replication;
-
- @Inject
- private ProjectCache projectCache;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- AdminReplicate.this.schedule();
- }
- });
- }
-
- private void schedule() throws Failure {
- if (all && projectNames.size() > 0) {
- throw new Failure(1, "error: cannot combine --all and PROJECT");
- }
-
- if (!replication.isEnabled()) {
- throw new Failure(1, "error: replication not enabled");
- }
-
- if (all) {
- pushAllOpFactory.create(urlMatch).start(0, TimeUnit.SECONDS);
-
- } else {
- for (final String name : projectNames) {
- final Project.NameKey key = new Project.NameKey(name);
- if (projectCache.get(key) != null) {
- replication.scheduleFullSync(key, urlMatch);
- } else {
- throw new Failure(1, "error: '" + name + "': not a Gerrit project");
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowCaches.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowCaches.java
deleted file mode 100644
index 1f2a6bd8be..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowCaches.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.AdminCommand;
-
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Statistics;
-import net.sf.ehcache.config.CacheConfiguration;
-
-import org.eclipse.jgit.lib.WindowCacheStatAccessor;
-
-import java.io.PrintWriter;
-
-/** Show the current cache states. */
-@AdminCommand
-final class AdminShowCaches extends CacheCommand {
- private PrintWriter p;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- display();
- }
- });
- }
-
- private void display() {
- p = toPrintWriter(out);
-
- p.print(String.format(//
- "%1s %-18s %-4s|%-20s| %-5s |%-14s|\n" //
- , "" //
- , "Name" //
- , "Max" //
- , "Object Count" //
- , "AvgGet" //
- , "Hit Ratio" //
- ));
- p.print(String.format(//
- "%1s %-18s %-4s|%6s %6s %6s| %-5s |%-4s %-4s %-4s|\n" //
- , "" //
- , "" //
- , "Age" //
- , "Disk" //
- , "Mem" //
- , "Cnt" //
- , "" //
- , "Disk" //
- , "Mem" //
- , "Agg" //
- ));
- p.println("------------------"
- + "-------+--------------------+----------+--------------+");
- for (final Ehcache cache : getAllCaches()) {
- final CacheConfiguration cfg = cache.getCacheConfiguration();
- final boolean useDisk = cfg.isDiskPersistent() || cfg.isOverflowToDisk();
- final Statistics stat = cache.getStatistics();
- final long total = stat.getCacheHits() + stat.getCacheMisses();
-
- if (useDisk) {
- p.print(String.format(//
- "D %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
- , cache.getName() //
- , interval(cfg.getTimeToLiveSeconds()) //
- , count(stat.getDiskStoreObjectCount()) //
- , count(stat.getMemoryStoreObjectCount()) //
- , count(stat.getObjectCount()) //
- , duration(stat.getAverageGetTime()) //
- , percent(stat.getOnDiskHits(), total) //
- , percent(stat.getInMemoryHits(), total) //
- , percent(stat.getCacheHits(), total) //
- ));
- } else {
- p.print(String.format(//
- " %-18s %-4s|%6s %6s %6s| %7s |%4s %4s %4s|\n" //
- , cache.getName() //
- , interval(cfg.getTimeToLiveSeconds()) //
- , "", "" //
- , count(stat.getObjectCount()) //
- , duration(stat.getAverageGetTime()) //
- , "", "" //
- , percent(stat.getCacheHits(), total) //
- ));
- }
- }
- p.println();
-
- final Runtime r = Runtime.getRuntime();
- final long mMax = r.maxMemory();
- final long mFree = r.freeMemory();
- final long mTotal = r.totalMemory();
- final long mInuse = mTotal - mFree;
- final long jgitBytes = WindowCacheStatAccessor.getOpenBytes();
-
- p.println("JGit Buffer Cache:");
- fItemCount("open files", WindowCacheStatAccessor.getOpenFiles());
- fByteCount("loaded", jgitBytes);
- fPercent("mem%", jgitBytes, mTotal);
- p.println();
-
- p.println("JVM Heap:");
- fByteCount("max", mMax);
- fByteCount("inuse", mInuse);
- fPercent("mem%", mInuse, mTotal);
- p.println();
-
- p.flush();
- }
-
- private void fItemCount(final String name, final long value) {
- p.println(String.format(" %1$-12s: %2$15d", name, value));
- }
-
- private void fByteCount(final String name, double value) {
- String suffix = "bytes";
- if (value > 1024) {
- value /= 1024;
- suffix = "kb";
- }
- if (value > 1024) {
- value /= 1024;
- suffix = "mb";
- }
- if (value > 1024) {
- value /= 1024;
- suffix = "gb";
- }
- p.println(String.format(" %1$-12s: %2$6.2f %3$s", name, value, suffix));
- }
-
- private String count(long cnt) {
- if (cnt == 0) {
- return "";
- }
- return String.format("%6d", cnt);
- }
-
- private String duration(double ms) {
- if (Math.abs(ms) <= 0.05) {
- return "";
- }
- String suffix = "ms";
- if (ms >= 1000) {
- ms /= 1000;
- suffix = "s ";
- }
- return String.format("%4.1f%s", ms, suffix);
- }
-
- private String interval(double ttl) {
- if (ttl == 0) {
- return "inf";
- }
-
- String suffix = "s";
- if (ttl >= 60) {
- ttl /= 60;
- suffix = "m";
-
- if (ttl >= 60) {
- ttl /= 60;
- suffix = "h";
- }
-
- if (ttl >= 24) {
- ttl /= 24;
- suffix = "d";
-
- if (ttl >= 365) {
- ttl /= 365;
- suffix = "y";
- }
- }
- }
-
- return Integer.toString((int) ttl) + suffix;
- }
-
- private String percent(final long value, final long total) {
- if (total <= 0) {
- return "";
- }
- final long pcent = (100 * value) / total;
- return String.format("%3d%%", (int) pcent);
- }
-
- private void fPercent(final String name, final long value, final long total) {
- final long pcent = 0 < total ? (100 * value) / total : 0;
- p.println(String.format(" %1$-12s: %2$3d%%", name, (int) pcent));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowConnections.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowConnections.java
deleted file mode 100644
index 767935d1b6..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowConnections.java
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.server.ssh.AdminCommand;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.gerrit.server.ssh.SshDaemon;
-import com.google.gerrit.server.ssh.SshUtil;
-import com.google.inject.Inject;
-
-import org.apache.mina.core.service.IoAcceptor;
-import org.apache.mina.core.session.IoSession;
-import org.apache.sshd.server.CommandFactory.Command;
-import org.apache.sshd.server.session.ServerSession;
-import org.kohsuke.args4j.Option;
-
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-
-/** Show the current SSH connections. */
-@AdminCommand
-final class AdminShowConnections extends BaseCommand {
- @Option(name = "--numeric", aliases = {"-n"}, usage = "don't resolve names")
- private boolean numeric;
-
- private PrintWriter p;
-
- @Inject
- private SshDaemon daemon;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- AdminShowConnections.this.display();
- }
- });
- }
-
- private void display() throws Failure {
- p = toPrintWriter(out);
-
- final IoAcceptor acceptor = daemon.getIoAcceptor();
- if (acceptor == null) {
- throw new Failure(1, "fatal: sshd no longer running");
- }
-
- final List<IoSession> list =
- new ArrayList<IoSession>(acceptor.getManagedSessions().values());
- Collections.sort(list, new Comparator<IoSession>() {
- @Override
- public int compare(IoSession arg0, IoSession arg1) {
- if (arg0.getCreationTime() < arg1.getCreationTime()) {
- return -1;
- } else if (arg0.getCreationTime() > arg1.getCreationTime()) {
- return 1;
- }
- return (int) (arg0.getId() - arg1.getId());
- }
- });
-
- final long now = System.currentTimeMillis();
- p.print(String.format(" %8s %8s %-15s %s\n", "Start", "Idle", "User",
- "Remote Host"));
- p.print("--------------------------------------------------------------\n");
- for (final IoSession io : list) {
- ServerSession s = (ServerSession) ServerSession.getSession(io, true);
- List<Command> active = s != null ? s.getAttribute(SshUtil.ACTIVE) : null;
-
- final SocketAddress remoteAddress = io.getRemoteAddress();
- final long start = io.getCreationTime();
- final long idle = now - io.getLastIoTime();
-
- p.print(String.format(" %8s %8s %-15.15s %.30s\n", time(now, start),
- age(idle), username(s), hostname(remoteAddress)));
- if (active != null) {
- synchronized (active) {
- for (final Command cmd : active) {
- p.print(String.format(" [ %s ]\n", cmd.toString()));
- }
- }
- }
- p.print("\n");
- }
- p.print("--\n");
-
- p.flush();
- }
-
- private static String time(final long now, final long time) {
- if (time - now < 24 * 60 * 60 * 1000L) {
- return new SimpleDateFormat("HH:mm:ss").format(new Date(time));
- }
- return new SimpleDateFormat("MMM-dd").format(new Date(time));
- }
-
- private static String age(long age) {
- age /= 1000;
-
- final int sec = (int) (age % 60);
- age /= 60;
-
- final int min = (int) (age % 60);
- age /= 60;
-
- final int hr = (int) (age % 60);
- return String.format("%02d:%02d:%02d", hr, min, sec);
- }
-
- private String username(final ServerSession s) {
- if (s == null) {
- return "";
- } else if (numeric) {
- final Account.Id id = s.getAttribute(SshUtil.CURRENT_ACCOUNT);
- return id != null ? "a/" + id.toString() : "";
- } else {
- final String user = s.getUsername();
- return user != null ? user : "";
- }
- }
-
- private String hostname(final SocketAddress remoteAddress) {
- if (remoteAddress == null) {
- return "?";
- }
- String host = null;
- if (remoteAddress instanceof InetSocketAddress) {
- final InetSocketAddress sa = (InetSocketAddress) remoteAddress;
- final InetAddress in = sa.getAddress();
- if (numeric) {
- return in.getHostAddress();
- }
- if (in != null) {
- host = in.getCanonicalHostName();
- } else {
- host = sa.getHostName();
- }
- }
- if (host == null) {
- host = remoteAddress.toString();
- }
- return host;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowQueue.java b/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowQueue.java
deleted file mode 100644
index 7f1373e21f..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/AdminShowQueue.java
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.git.WorkQueue;
-import com.google.gerrit.git.WorkQueue.Task;
-import com.google.gerrit.server.ssh.AdminCommand;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.inject.Inject;
-
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/** Display the current work queue. */
-@AdminCommand
-final class AdminShowQueue extends BaseCommand {
- @Inject
- private WorkQueue workQueue;
-
- private PrintWriter p;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- AdminShowQueue.this.display();
- }
- });
- }
-
- private void display() {
- p = toPrintWriter(out);
-
- final List<Task<?>> pending = workQueue.getTasks();
- Collections.sort(pending, new Comparator<Task<?>>() {
- public int compare(Task<?> a, Task<?> b) {
- final Task.State aState = a.getState();
- final Task.State bState = b.getState();
-
- if (aState != bState) {
- return aState.ordinal() - bState.ordinal();
- }
-
- final long aDelay = a.getDelay(TimeUnit.MILLISECONDS);
- final long bDelay = b.getDelay(TimeUnit.MILLISECONDS);
-
- if (aDelay < bDelay) {
- return -1;
- } else if (aDelay > bDelay) {
- return 1;
- }
- return format(a).compareTo(format(b));
- }
- });
-
- p.print(String.format(" %1s %-12s %s\n", "S", "Start", "Task"));
- p.print("--------------------------------------------------------------\n");
-
- final long now = System.currentTimeMillis();
- for (final Task<?> task : pending) {
- final long delay = task.getDelay(TimeUnit.MILLISECONDS);
- final Task.State state = task.getState();
-
- final String start;
- switch (state) {
- case DONE:
- case CANCELLED:
- case RUNNING:
- case READY:
- start = "";
- break;
- default:
- start = time(now, delay);
- break;
- }
-
- p.print(String.format(" %1s %12s %s\n", format(state), start,
- format(task)));
- }
- p.print("--------------------------------------------------------------\n");
- p.print(" " + pending.size() + " tasks\n");
-
- p.flush();
- }
-
- private static String time(final long now, final long delay) {
- final Date when = new Date(now + delay);
- if (delay < 24 * 60 * 60 * 1000L) {
- return new SimpleDateFormat("HH:mm:ss.SSS").format(when);
- }
- return new SimpleDateFormat("MMM-dd HH:mm").format(when);
- }
-
- private static String format(final Task<?> task) {
- return task.getRunnable().toString();
- }
-
- private static String format(final Task.State state) {
- switch (state) {
- case DONE:
- return "D";
- case CANCELLED:
- return "C";
- case RUNNING:
- return "R";
- case READY:
- return "W";
- case SLEEPING:
- return "S";
- default:
- return " ";
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/ApproveCommand.java b/src/main/java/com/google/gerrit/server/ssh/commands/ApproveCommand.java
deleted file mode 100644
index e8bd08006b..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/ApproveCommand.java
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.RevId;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.pgm.CmdLineParser;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.mail.CommentSender;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
-import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.gerrit.server.workflow.FunctionState;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.ResultSet;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-
-import org.kohsuke.args4j.Argument;
-import org.kohsuke.args4j.Option;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class ApproveCommand extends BaseCommand {
- private static final Logger log =
- LoggerFactory.getLogger(ApproveCommand.class);
-
- @Override
- protected final CmdLineParser newCmdLineParser() {
- final CmdLineParser parser = super.newCmdLineParser();
- for (ApproveOption c : optionList) {
- parser.addOption(c, c);
- }
- return parser;
- }
-
- private final Set<PatchSet.Id> patchSetIds = new HashSet<PatchSet.Id>();
-
- @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "patch to approve")
- void addPatchSetId(final String token) {
- try {
- patchSetIds.addAll(parsePatchSetId(token));
- } catch (UnloggedFailure e) {
- throw new IllegalArgumentException(e.getMessage(), e);
- } catch (OrmException e) {
- throw new IllegalArgumentException("database error", e);
- }
- }
-
- @Option(name = "--project", aliases = "-p", usage = "project containing the patch set")
- private ProjectControl projectControl;
-
- @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change", metaVar = "MESSAGE")
- private String changeComment;
-
- @Inject
- private ReviewDb db;
-
- @Inject
- private IdentifiedUser currentUser;
-
- @Inject
- private CommentSender.Factory commentSenderFactory;
-
- @Inject
- private PatchSetInfoFactory patchSetInfoFactory;
-
- @Inject
- private ApprovalTypes approvalTypes;
-
- @Inject
- private ChangeControl.Factory changeControlFactory;
-
- @Inject
- private FunctionState.Factory functionStateFactory;
-
- private List<ApproveOption> optionList;
-
- @Override
- public final void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Failure {
- initOptionList();
- parseCommandLine();
-
- boolean ok = true;
- for (final PatchSet.Id patchSetId : patchSetIds) {
- try {
- approveOne(patchSetId);
- } catch (UnloggedFailure e) {
- ok = false;
- writeError("error: " + e.getMessage() + "\n");
- } catch (Exception e) {
- ok = false;
- writeError("fatal: internal server error while approving "
- + patchSetId + "\n");
- log.error("internal error while approving " + patchSetId);
- }
- }
- if (!ok) {
- throw new UnloggedFailure(1, "one or more approvals failed;"
- + " review output above");
- }
- }
- });
- }
-
- private void approveOne(final PatchSet.Id patchSetId)
- throws NoSuchChangeException, UnloggedFailure, OrmException,
- PatchSetInfoNotAvailableException, EmailException {
- final Change.Id changeId = patchSetId.getParentKey();
- final ChangeControl changeControl =
- changeControlFactory.validateFor(changeId);
- final Change change = changeControl.getChange();
- if (change.getStatus().isClosed()) {
- throw error("change " + changeId + " is closed");
- }
-
- final Transaction txn = db.beginTransaction();
- final StringBuffer msgBuf = new StringBuffer();
- msgBuf.append("Patch Set ");
- msgBuf.append(patchSetId.get());
- msgBuf.append(": ");
-
- for (ApproveOption co : optionList) {
- final ApprovalCategory.Id category = co.getCategoryId();
- PatchSetApproval.Key psaKey =
- new PatchSetApproval.Key(patchSetId, currentUser.getAccountId(),
- category);
- PatchSetApproval psa = db.patchSetApprovals().get(psaKey);
-
- Short score = co.value();
-
- if (score != null) {
- addApproval(psaKey, score, change, co, txn);
- } else {
- if (psa == null) {
- score = 0;
- addApproval(psaKey, score, change, co, txn);
- } else {
- score = psa.getValue();
- }
- }
-
- String message =
- db.approvalCategoryValues().get(
- new ApprovalCategoryValue.Id(category, score)).getName();
- msgBuf.append(" " + message + ";");
- }
-
- msgBuf.deleteCharAt(msgBuf.length() - 1);
- msgBuf.append("\n\n");
-
- if (changeComment != null) {
- msgBuf.append(changeComment);
- }
-
- String uuid = ChangeUtil.messageUUID(db);
- ChangeMessage cm =
- new ChangeMessage(new ChangeMessage.Key(changeId, uuid), currentUser
- .getAccountId());
- cm.setMessage(msgBuf.toString());
- db.changeMessages().insert(Collections.singleton(cm), txn);
- ChangeUtil.updated(change);
- db.changes().update(Collections.singleton(change), txn);
- txn.commit();
- sendMail(change, change.currentPatchSetId(), cm);
- }
-
- private Set<PatchSet.Id> parsePatchSetId(final String patchIdentity)
- throws UnloggedFailure, OrmException {
- // By commit?
- //
- if (patchIdentity.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
- final RevId id = new RevId(patchIdentity);
- final ResultSet<PatchSet> patches;
- if (id.isComplete()) {
- patches = db.patchSets().byRevision(id);
- } else {
- patches = db.patchSets().byRevisionRange(id, id.max());
- }
-
- final Set<PatchSet.Id> matches = new HashSet<PatchSet.Id>();
- for (final PatchSet ps : patches) {
- final Change change = db.changes().get(ps.getId().getParentKey());
- if (inProject(change)) {
- matches.add(ps.getId());
- }
- }
-
- switch (matches.size()) {
- case 1:
- return matches;
- case 0:
- throw error("\"" + patchIdentity + "\" no such patch set");
- default:
- throw error("\"" + patchIdentity + "\" matches multiple patch sets");
- }
- }
-
- // By older style change,patchset?
- //
- if (patchIdentity.matches("^[1-9][0-9]*,[1-9][0-9]*$")) {
- final PatchSet.Id patchSetId;
- try {
- patchSetId = PatchSet.Id.parse(patchIdentity);
- } catch (IllegalArgumentException e) {
- throw error("\"" + patchIdentity + "\" is not a valid patch set");
- }
- if (db.patchSets().get(patchSetId) == null) {
- throw error("\"" + patchIdentity + "\" no such patch set");
- }
- if (projectControl != null) {
- final Change change = db.changes().get(patchSetId.getParentKey());
- if (!inProject(change)) {
- throw error("change " + change.getId() + " not in project "
- + projectControl.getProject().getName());
- }
- }
- return Collections.singleton(patchSetId);
- }
-
- throw error("\"" + patchIdentity + "\" is not a valid patch set");
- }
-
- private boolean inProject(final Change change) {
- if (projectControl == null) {
- // No --project option, so they want every project.
- return true;
- }
- return projectControl.getProject().getNameKey().equals(change.getProject());
- }
-
- private void sendMail(final Change c, final PatchSet.Id psid,
- final ChangeMessage message) throws PatchSetInfoNotAvailableException,
- EmailException, OrmException {
- PatchSet ps = db.patchSets().get(psid);
- final CommentSender cm;
- cm = commentSenderFactory.create(c);
- cm.setFrom(currentUser.getAccountId());
- cm.setPatchSet(ps, patchSetInfoFactory.get(psid));
- cm.setChangeMessage(message);
- cm.setReviewDb(db);
- cm.send();
- }
-
- private void addApproval(final PatchSetApproval.Key psaKey,
- final Short score, final Change c, final ApproveOption co,
- final Transaction txn) throws OrmException, UnloggedFailure {
- PatchSetApproval psa = db.patchSetApprovals().get(psaKey);
- boolean insert = false;
-
- if (psa == null) {
- insert = true;
- psa = new PatchSetApproval(psaKey, score);
- }
-
- final List<PatchSetApproval> approvals = Collections.emptyList();
- final FunctionState fs =
- functionStateFactory.create(c, psaKey.getParentKey(), approvals);
- psa.setValue(score);
- fs.normalize(approvalTypes.getApprovalType(psa.getCategoryId()), psa);
- if (score != psa.getValue()) {
- throw error(co.name() + "=" + co.value() + " not permitted");
- }
-
- psa.setGranted();
-
- if (insert) {
- db.patchSetApprovals().insert(Collections.singleton(psa), txn);
- } else {
- db.patchSetApprovals().update(Collections.singleton(psa), txn);
- }
- }
-
- private void initOptionList() {
- optionList = new ArrayList<ApproveOption>();
-
- for (ApprovalType type : approvalTypes.getApprovalTypes()) {
- String usage = "";
- final ApprovalCategory category = type.getCategory();
- usage = "score for " + category.getName() + "\n";
-
- for (ApprovalCategoryValue v : type.getValues()) {
- usage += v.format() + "\n";
- }
-
- final String name =
- "--" + category.getName().toLowerCase().replace(' ', '-');
- optionList.add(new ApproveOption(name, usage, type));
- }
- }
-
- private void writeError(final String msg) {
- try {
- err.write(msg.getBytes(ENC));
- } catch (IOException e) {
- }
- }
-
- private static UnloggedFailure error(final String msg) {
- return new UnloggedFailure(1, msg);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/ApproveOption.java b/src/main/java/com/google/gerrit/server/ssh/commands/ApproveOption.java
deleted file mode 100644
index 731ca1cc6c..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/ApproveOption.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-
-import org.kohsuke.args4j.CmdLineException;
-import org.kohsuke.args4j.CmdLineParser;
-import org.kohsuke.args4j.Option;
-import org.kohsuke.args4j.OptionDef;
-import org.kohsuke.args4j.spi.OneArgumentOptionHandler;
-import org.kohsuke.args4j.spi.OptionHandler;
-import org.kohsuke.args4j.spi.Setter;
-
-import java.lang.annotation.Annotation;
-
-final class ApproveOption implements Option, Setter<Short> {
- private final String name;
- private final String usage;
- private final ApprovalType type;
-
- private Short value;
-
- ApproveOption(final String name, final String usage, final ApprovalType type) {
- this.name = name;
- this.usage = usage;
- this.type = type;
- }
-
- @Override
- public String[] aliases() {
- return new String[0];
- }
-
- @Override
- public Class<? extends OptionHandler<Short>> handler() {
- return Handler.class;
- }
-
- @Override
- public String metaVar() {
- return "N";
- }
-
- @Override
- public boolean multiValued() {
- return false;
- }
-
- @Override
- public String name() {
- return name;
- }
-
- @Override
- public boolean required() {
- return false;
- }
-
- @Override
- public String usage() {
- return usage;
- }
-
- public Short value() {
- return value;
- }
-
- @Override
- public Class<? extends Annotation> annotationType() {
- return null;
- }
-
- @Override
- public void addValue(final Short val) {
- this.value = val;
- }
-
- @Override
- public Class<Short> getType() {
- return Short.class;
- }
-
- @Override
- public boolean isMultiValued() {
- return false;
- }
-
- ApprovalCategory.Id getCategoryId() {
- return type.getCategory().getId();
- }
-
- public static class Handler extends OneArgumentOptionHandler<Short> {
- private final ApproveOption cmdOption;
-
- public Handler(final CmdLineParser parser, final OptionDef option,
- final Setter<Short> setter) {
- super(parser, option, setter);
- this.cmdOption = (ApproveOption) setter;
- }
-
- @Override
- protected Short parse(final String token) throws NumberFormatException,
- CmdLineException {
- String argument = token;
- if (argument.startsWith("+")) {
- argument = argument.substring(1);
- }
-
- final short value = Short.parseShort(argument);
- final ApprovalCategoryValue min = cmdOption.type.getMin();
- final ApprovalCategoryValue max = cmdOption.type.getMax();
-
- if (value < min.getValue() || value > max.getValue()) {
- final String name = cmdOption.name();
- final String e =
- "\"" + token + "\" must be in range " + min.formatValue() + ".."
- + max.formatValue() + " for \"" + name + "\"";
- throw new CmdLineException(owner, e);
- }
- return value;
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/CacheCommand.java b/src/main/java/com/google/gerrit/server/ssh/commands/CacheCommand.java
deleted file mode 100644
index 2e7e8d146a..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/CacheCommand.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.cache.CachePool;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.inject.Inject;
-
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-
-import java.util.Arrays;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-abstract class CacheCommand extends BaseCommand {
- @Inject
- protected CachePool cachePool;
-
- protected SortedSet<String> cacheNames() {
- final SortedSet<String> names = new TreeSet<String>();
- for (final Ehcache c : getAllCaches()) {
- names.add(c.getName());
- }
- return names;
- }
-
- protected Ehcache[] getAllCaches() {
- final CacheManager cacheMgr = cachePool.getCacheManager();
- final String[] cacheNames = cacheMgr.getCacheNames();
- Arrays.sort(cacheNames);
- final Ehcache[] r = new Ehcache[cacheNames.length];
- for (int i = 0; i < cacheNames.length; i++) {
- r[i] = cacheMgr.getEhcache(cacheNames[i]);
- }
- return r;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/DefaultCommandModule.java b/src/main/java/com/google/gerrit/server/ssh/commands/DefaultCommandModule.java
deleted file mode 100644
index 19cbf90a62..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/DefaultCommandModule.java
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.CommandModule;
-import com.google.gerrit.server.ssh.CommandName;
-import com.google.gerrit.server.ssh.Commands;
-import com.google.gerrit.server.ssh.DispatchCommandProvider;
-
-
-/** Register the basic commands any Gerrit server should support. */
-public class DefaultCommandModule extends CommandModule {
- @Override
- protected void configure() {
- final CommandName git = Commands.named("git");
- final CommandName gerrit = Commands.named("gerrit");
-
- // The following commands can be ran on a server in either Master or Slave
- // mode. If a command should only be used on a server in one mode, but not
- // both, it should be bound in both MasterCommandModule and
- // SlaveCommandModule.
-
- command(gerrit).toProvider(new DispatchCommandProvider(gerrit));
- command(gerrit, "flush-caches").to(AdminFlushCaches.class);
- command(gerrit, "ls-projects").to(ListProjects.class);
- command(gerrit, "show-caches").to(AdminShowCaches.class);
- command(gerrit, "show-connections").to(AdminShowConnections.class);
- command(gerrit, "show-queue").to(AdminShowQueue.class);
-
- command(git).toProvider(new DispatchCommandProvider(git));
- command(git, "receive-pack").to(Commands.key(gerrit, "receive-pack"));
- command(git, "upload-pack").to(Upload.class);
-
- command("scp").to(ScpCommand.class);
-
- // Honor the legacy hyphenated forms as aliases for the non-hyphenated forms
- //
- command("git-upload-pack").to(Commands.key(git, "upload-pack"));
- command("git-receive-pack").to(Commands.key(git, "receive-pack"));
- command("gerrit-receive-pack").to(Commands.key(git, "receive-pack"));
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/ErrorSlaveMode.java b/src/main/java/com/google/gerrit/server/ssh/commands/ErrorSlaveMode.java
deleted file mode 100644
index 3226b863ae..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/ErrorSlaveMode.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.BaseCommand;
-
-import java.io.IOException;
-
-/**
- * A command which just throws an error because it shouldn't be ran on this
- * server. This is used when a user tries to run a command on a server in Slave
- * Mode, but the command only applies to the Master server.
- */
-final class ErrorSlaveMode extends BaseCommand {
- @Override
- public void start() {
- String msg =
- "error: That command is disabled on this server.\n\n"
- + "Please use the master server URL.\n";
- try {
- err.write(msg.getBytes(ENC));
- err.flush();
- } catch (IOException e) {
- // Ignore errors writing to the client
- }
- onExit(1);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/ListProjects.java b/src/main/java/com/google/gerrit/server/ssh/commands/ListProjects.java
deleted file mode 100644
index 3274464811..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/ListProjects.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.config.WildProjectName;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.ssh.BaseCommand;
-import com.google.gwtorm.client.OrmException;
-import com.google.inject.Inject;
-
-import java.io.PrintWriter;
-
-final class ListProjects extends BaseCommand {
- @Inject
- private ReviewDb db;
-
- @Inject
- private IdentifiedUser currentUser;
-
- @Inject
- private ProjectCache projectCache;
-
- @Inject
- @WildProjectName
- private Project.NameKey wildProject;
-
- @Override
- public void start() {
- startThread(new CommandRunnable() {
- @Override
- public void run() throws Exception {
- parseCommandLine();
- ListProjects.this.display();
- }
- });
- }
-
- private void display() throws Failure {
- final PrintWriter stdout = toPrintWriter(out);
- try {
- for (final Project p : db.projects().all()) {
- if (p.getNameKey().equals(wildProject)) {
- // This project "doesn't exist". At least not as a repository.
- //
- continue;
- }
-
- final ProjectState e = projectCache.get(p.getNameKey());
- if (e != null && e.controlFor(currentUser).isVisible()) {
- stdout.print(p.getName());
- stdout.println();
- }
- }
- } catch (OrmException e) {
- throw new Failure(1, "fatal: database error", e);
- } finally {
- stdout.flush();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/MasterCommandModule.java b/src/main/java/com/google/gerrit/server/ssh/commands/MasterCommandModule.java
deleted file mode 100644
index 47e056f32e..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/MasterCommandModule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.CommandModule;
-import com.google.gerrit.server.ssh.CommandName;
-import com.google.gerrit.server.ssh.Commands;
-
-
-/** Register the commands a Gerrit server in master mode supports. */
-public class MasterCommandModule extends CommandModule {
- @Override
- protected void configure() {
- final CommandName gerrit = Commands.named("gerrit");
-
- command(gerrit, "approve").to(ApproveCommand.class);
- command(gerrit, "create-project").to(AdminCreateProject.class);
- command(gerrit, "receive-pack").to(Receive.class);
- command(gerrit, "replicate").to(AdminReplicate.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/Receive.java b/src/main/java/com/google/gerrit/server/ssh/commands/Receive.java
deleted file mode 100644
index a0ebb169fe..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/Receive.java
+++ /dev/null
@@ -1,1440 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_HEAD_UPDATE;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_TAG;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_TAG_ANNOTATED;
-import static com.google.gerrit.client.reviewdb.ApprovalCategory.PUSH_TAG_ANY;
-
-import com.google.gerrit.client.Link;
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AbstractAgreement;
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountAgreement;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.AccountGroupAgreement;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Branch;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ChangeMessage;
-import com.google.gerrit.client.reviewdb.ContributorAgreement;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.PatchSetInfo;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.rpc.NoSuchAccountException;
-import com.google.gerrit.git.PatchSetImporter;
-import com.google.gerrit.git.ReplicationQueue;
-import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountResolver;
-import com.google.gerrit.server.config.CanonicalWebUrl;
-import com.google.gerrit.server.config.Nullable;
-import com.google.gerrit.server.mail.CreateChangeSender;
-import com.google.gerrit.server.mail.EmailException;
-import com.google.gerrit.server.mail.MergedSender;
-import com.google.gerrit.server.mail.ReplacePatchSetSender;
-import com.google.gerrit.server.patch.PatchSetInfoFactory;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.OrmRunnable;
-import com.google.gwtorm.client.Transaction;
-import com.google.inject.Inject;
-
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.revwalk.FooterKey;
-import org.eclipse.jgit.revwalk.FooterLine;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevFlag;
-import org.eclipse.jgit.revwalk.RevFlagSet;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevTag;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.eclipse.jgit.transport.PostReceiveHook;
-import org.eclipse.jgit.transport.PreReceiveHook;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceivePack;
-import org.eclipse.jgit.transport.ReceiveCommand.Result;
-import org.eclipse.jgit.transport.ReceiveCommand.Type;
-import org.kohsuke.args4j.Option;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
-
-/** Receives change upload over SSH using the Git receive-pack protocol. */
-final class Receive extends AbstractGitCommand {
- private static final Logger log = LoggerFactory.getLogger(Receive.class);
-
- private static final String NEW_CHANGE = "refs/for/";
- private static final Pattern NEW_PATCHSET =
- Pattern.compile("^refs/changes/(?:[0-9][0-9]/)?([1-9][0-9]*)(?:/new)?$");
-
- private static final FooterKey REVIEWED_BY = new FooterKey("Reviewed-by");
- private static final FooterKey TESTED_BY = new FooterKey("Tested-by");
- private static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
-
- private final Set<Account.Id> reviewerId = new HashSet<Account.Id>();
- private final Set<Account.Id> ccId = new HashSet<Account.Id>();
-
- @Option(name = "--reviewer", aliases = {"--re"}, multiValued = true, metaVar = "EMAIL", usage = "request reviewer for change(s)")
- void addReviewer(final Account.Id id) {
- reviewerId.add(id);
- }
-
- @Option(name = "--cc", aliases = {}, multiValued = true, metaVar = "EMAIL", usage = "CC user on change(s)")
- void addCC(final Account.Id id) {
- ccId.add(id);
- }
-
- @Inject
- private IdentifiedUser.GenericFactory identifiedUserFactory;
-
- @Inject
- private IdentifiedUser currentUser;
-
- @Inject
- private ReviewDb db;
-
- @Inject
- private ApprovalTypes approvalTypes;
-
- @Inject
- private AccountResolver accountResolver;
-
- @Inject
- private CreateChangeSender.Factory createChangeSenderFactory;
-
- @Inject
- private MergedSender.Factory mergedSenderFactory;
-
- @Inject
- private ReplacePatchSetSender.Factory replacePatchSetFactory;
-
- @Inject
- private ReplicationQueue replication;
-
- @Inject
- private PatchSetImporter.Factory importFactory;
-
- @Inject
- private PatchSetInfoFactory patchSetInfoFactory;
-
- @Inject
- @CanonicalWebUrl
- @Nullable
- private String canonicalWebUrl;
-
- @Inject
- @GerritPersonIdent
- private PersonIdent gerritIdent;
-
- private ReceivePack rp;
- private PersonIdent refLogIdent;
- private ReceiveCommand newChange;
- private Branch.NameKey destBranch;
-
- private final List<Change.Id> allNewChanges = new ArrayList<Change.Id>();
- private final Map<Change.Id, ReplaceRequest> replaceByChange =
- new HashMap<Change.Id, ReplaceRequest>();
- private final Map<RevCommit, ReplaceRequest> replaceByCommit =
- new HashMap<RevCommit, ReplaceRequest>();
-
- private Map<ObjectId, Ref> refsById;
-
- protected boolean canUpload() {
- return canPerform(ApprovalCategory.READ, (short) 2);
- }
-
- @Override
- protected void runImpl() throws IOException, Failure {
- if (!canUpload()) {
- final String reqName = project.getName();
- throw new Failure(1, "fatal: Upload denied for project '" + reqName + "'",
- new SecurityException("Account lacks Upload permission"));
- }
-
- if (project.isUseContributorAgreements()) {
- verifyActiveContributorAgreement();
- }
- refLogIdent = currentUser.newPersonIdent();
-
- verifyProjectVisible("reviewer", reviewerId);
- verifyProjectVisible("CC", ccId);
-
- rp = new ReceivePack(repo);
- rp.setAllowCreates(true);
- rp.setAllowDeletes(true);
- rp.setAllowNonFastForwards(true);
- rp.setCheckReceivedObjects(true);
- rp.setRefLogIdent(refLogIdent);
- rp.setPreReceiveHook(new PreReceiveHook() {
- public void onPreReceive(final ReceivePack arg0,
- final Collection<ReceiveCommand> commands) {
- parseCommands(commands);
- if (newChange != null
- && newChange.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
- createNewChanges();
- }
- doReplaces();
- }
- });
- rp.setPostReceiveHook(new PostReceiveHook() {
- public void onPostReceive(final ReceivePack arg0,
- final Collection<ReceiveCommand> commands) {
- for (final ReceiveCommand c : commands) {
- if (c.getResult() == Result.OK) {
- if (isHead(c)) {
- switch (c.getType()) {
- case CREATE:
- autoCloseChanges(c);
- break;
- case DELETE:
- break;
- case UPDATE:
- case UPDATE_NONFASTFORWARD:
- autoCloseChanges(c);
- break;
- }
- }
-
- if (isHead(c) || isTag(c)) {
- // We only schedule heads and tags for replication.
- // Change refs are scheduled when they are created.
- //
- replication.scheduleUpdate(project.getNameKey(), c.getRefName());
- }
- }
- }
- }
- });
- rp.receive(in, out, err);
-
- if (!allNewChanges.isEmpty() && canonicalWebUrl != null) {
- // Make sure there isn't anything buffered; we want to give the
- // push client a chance to display its status report before we
- // show our own messages on standard error.
- //
- out.flush();
-
- final String url = canonicalWebUrl;
- final PrintWriter msg = toPrintWriter(err);
- msg.write("\nNew Changes:\n");
- for (final Change.Id c : allNewChanges) {
- msg.write(" " + url + c.get() + "\n");
- }
- msg.write('\n');
- msg.flush();
- }
- }
-
- private void verifyProjectVisible(final String type, final Set<Account.Id> who)
- throws UnloggedFailure {
- for (final Account.Id id : who) {
- final IdentifiedUser user = identifiedUserFactory.create(id);
- if (!projectControl.forUser(user).isVisible()) {
- throw new UnloggedFailure(1, type + " "
- + user.getAccount().getFullName() + " cannot access the project");
- }
- }
- }
-
- private void verifyActiveContributorAgreement() throws Failure {
- AbstractAgreement bestAgreement = null;
- ContributorAgreement bestCla = null;
- try {
- OUTER: for (AccountGroup.Id groupId : currentUser.getEffectiveGroups()) {
- for (final AccountGroupAgreement a : db.accountGroupAgreements()
- .byGroup(groupId)) {
- final ContributorAgreement cla =
- db.contributorAgreements().get(a.getAgreementId());
- if (cla == null) {
- continue;
- }
-
- bestAgreement = a;
- bestCla = cla;
- break OUTER;
- }
- }
-
- if (bestAgreement == null) {
- for (final AccountAgreement a : db.accountAgreements().byAccount(
- currentUser.getAccountId()).toList()) {
- final ContributorAgreement cla =
- db.contributorAgreements().get(a.getAgreementId());
- if (cla == null) {
- continue;
- }
-
- bestAgreement = a;
- bestCla = cla;
- break;
- }
- }
- } catch (OrmException e) {
- throw new Failure(1, "fatal: database error", e);
- }
-
- if (bestCla != null && !bestCla.isActive()) {
- final StringBuilder msg = new StringBuilder();
- msg.append("\nfatal: ");
- msg.append(bestCla.getShortName());
- msg.append(" contributor agreement is expired.\n");
- if (canonicalWebUrl != null) {
- msg.append("\nPlease complete a new agreement");
- msg.append(":\n\n ");
- msg.append(canonicalWebUrl);
- msg.append("#");
- msg.append(Link.SETTINGS_AGREEMENTS);
- msg.append("\n");
- }
- msg.append("\n");
- throw new UnloggedFailure(1, msg.toString());
- }
-
- if (bestCla != null && bestCla.isRequireContactInformation()) {
- boolean fail = false;
- fail |= missing(currentUser.getAccount().getFullName());
- fail |= missing(currentUser.getAccount().getPreferredEmail());
- fail |= !currentUser.getAccount().isContactFiled();
-
- if (fail) {
- final StringBuilder msg = new StringBuilder();
- msg.append("\nfatal: ");
- msg.append(bestCla.getShortName());
- msg.append(" contributor agreement requires");
- msg.append(" current contact information.\n");
- if (canonicalWebUrl != null) {
- msg.append("\nPlease review your contact information");
- msg.append(":\n\n ");
- msg.append(canonicalWebUrl);
- msg.append("#");
- msg.append(Link.SETTINGS_CONTACT);
- msg.append("\n");
- }
- msg.append("\n");
- throw new UnloggedFailure(1, msg.toString());
- }
- }
-
- if (bestAgreement != null) {
- switch (bestAgreement.getStatus()) {
- case VERIFIED:
- return;
- case REJECTED:
- throw new UnloggedFailure(1, "\nfatal: " + bestCla.getShortName()
- + " contributor agreement was rejected."
- + "\n (rejected on " + bestAgreement.getReviewedOn()
- + ")\n");
- case NEW:
- throw new UnloggedFailure(1, "\nfatal: " + bestCla.getShortName()
- + " contributor agreement is still pending review.\n");
- }
- }
-
- final StringBuilder msg = new StringBuilder();
- msg.append("\nfatal: A Contributor Agreement"
- + " must be completed before uploading");
- if (canonicalWebUrl != null) {
- msg.append(":\n\n ");
- msg.append(canonicalWebUrl);
- msg.append("#");
- msg.append(Link.SETTINGS_AGREEMENTS);
- msg.append("\n");
- } else {
- msg.append(".");
- }
- msg.append("\n");
- throw new UnloggedFailure(1, msg.toString());
- }
-
- private static boolean missing(final String value) {
- return value == null || value.trim().equals("");
- }
-
- private Account.Id toAccountId(final String nameOrEmail) throws OrmException,
- NoSuchAccountException {
- final Account a = accountResolver.find(nameOrEmail);
- if (a == null) {
- throw new NoSuchAccountException("\"" + nameOrEmail
- + "\" is not registered");
- }
- return a.getId();
- }
-
- private void parseCommands(final Collection<ReceiveCommand> commands) {
- for (final ReceiveCommand cmd : commands) {
- if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED) {
- // Already rejected by the core receive process.
- //
- continue;
- }
-
- if (cmd.getRefName().startsWith(NEW_CHANGE)) {
- parseNewChangeCommand(cmd);
- continue;
- }
-
- final Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
- if (m.matches()) {
- // The referenced change must exist and must still be open.
- //
- final Change.Id changeId = Change.Id.parse(m.group(1));
- parseReplaceCommand(cmd, changeId);
- continue;
- }
-
- switch (cmd.getType()) {
- case CREATE:
- parseCreate(cmd);
- continue;
-
- case UPDATE:
- parseUpdate(cmd);
- continue;
-
- case DELETE:
- case UPDATE_NONFASTFORWARD:
- parseRewindOrDelete(cmd);
- continue;
- }
-
- // Everything else is bogus as far as we are concerned.
- //
- reject(cmd);
- }
- }
-
- private void parseCreate(final ReceiveCommand cmd) {
- if (projectControl.canCreateRef(cmd.getRefName())) {
- if (isTag(cmd)) {
- parseCreateTag(cmd);
- }
-
- } else {
- reject(cmd);
- }
- }
-
- private void parseCreateTag(final ReceiveCommand cmd) {
- try {
- final RevObject obj = rp.getRevWalk().parseAny(cmd.getNewId());
- if (!(obj instanceof RevTag)) {
- reject(cmd, "not annotated tag");
- return;
- }
-
- if (canPerform(PUSH_TAG, PUSH_TAG_ANY)) {
- // If we can push any tag, validation is sufficient at this point.
- //
- return;
- }
-
- final RevTag tag = (RevTag) obj;
- final PersonIdent tagger = tag.getTaggerIdent();
- if (tagger == null) {
- reject(cmd, "no tagger");
- return;
- }
-
- final String email = tagger.getEmailAddress();
- if (!currentUser.getEmailAddresses().contains(email)) {
- reject(cmd, "invalid tagger " + email);
- return;
- }
-
- if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
- // Signed tags are currently assumed valid, as we don't have a GnuPG
- // key ring to validate them against, and we might be missing the
- // necessary (but currently optional) BouncyCastle Crypto libraries.
- //
- } else if (canPerform(PUSH_TAG, PUSH_TAG_ANNOTATED)) {
- // User is permitted to push an unsigned annotated tag.
- //
- } else {
- reject(cmd, "must be signed");
- return;
- }
-
- // Let the core receive process handle it
- //
- } catch (IOException e) {
- log.error("Bad tag " + cmd.getRefName() + " " + cmd.getNewId().name(), e);
- reject(cmd, "invalid object");
- }
- }
-
- private void parseUpdate(final ReceiveCommand cmd) {
- if (isHead(cmd) && canPerform(PUSH_HEAD, PUSH_HEAD_UPDATE)) {
- // Let the core receive process handle it
- } else {
- reject(cmd);
- }
- }
-
- private void parseRewindOrDelete(final ReceiveCommand cmd) {
- if (isHead(cmd) && cmd.getType() == Type.DELETE
- && projectControl.canDeleteRef(cmd.getRefName())) {
- // Let the core receive process handle it
-
- } else if (isHead(cmd) && canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE)) {
- // Let the core receive process handle it
-
- } else if (isHead(cmd) && cmd.getType() == Type.UPDATE_NONFASTFORWARD) {
- cmd.setResult(ReceiveCommand.Result.REJECTED_NONFASTFORWARD);
-
- } else {
- reject(cmd);
- }
- }
-
- private void parseNewChangeCommand(final ReceiveCommand cmd) {
- // Permit exactly one new change request per push.
- //
- if (newChange != null) {
- reject(cmd, "duplicate request");
- return;
- }
-
- newChange = cmd;
- String destBranchName = cmd.getRefName().substring(NEW_CHANGE.length());
- if (!destBranchName.startsWith(Constants.R_REFS)) {
- destBranchName = Constants.R_HEADS + destBranchName;
- }
-
- if (rp.getAdvertisedRefs().containsKey(destBranchName)) {
- // We advertised the branch to the client so we know
- // the branch exists. Target this branch for the upload.
- //
- destBranch = new Branch.NameKey(project.getNameKey(), destBranchName);
-
- } else {
- // We didn't advertise the branch, because it doesn't exist yet.
- // Allow it anyway if HEAD is a symbolic reference to the name.
- //
- final String head;
- try {
- head = repo.getFullBranch();
- } catch (IOException e) {
- log.error("Cannot read HEAD symref", e);
- reject(cmd, "internal error");
- return;
- }
-
- if (head.equals(destBranchName)) {
- destBranch = new Branch.NameKey(project.getNameKey(), destBranchName);
- }
- }
-
- if (destBranch == null) {
- String n = destBranchName;
- if (n.startsWith(Constants.R_HEADS))
- n = n.substring(Constants.R_HEADS.length());
- reject(cmd, "branch " + n + " not found");
- return;
- }
-
- // Validate that the new commits are connected with the existing heads
- // or tags of this repository. If they aren't, we want to abort. We do
- // this check by coloring the tip CONNECTED and letting a RevWalk push
- // that color through the graph until it reaches at least one of our
- // already existing heads or tags. We then test to see if that color
- // made it back onto that set.
- //
- try {
- final RevWalk walk = rp.getRevWalk();
-
- final RevFlag SIDE_NEW = walk.newFlag("NEW");
- final RevFlag SIDE_HAVE = walk.newFlag("HAVE");
- final RevFlagSet COMMON = new RevFlagSet();
- COMMON.add(SIDE_NEW);
- COMMON.add(SIDE_HAVE);
- walk.carry(COMMON);
-
- walk.reset();
- walk.sort(RevSort.TOPO);
- walk.sort(RevSort.REVERSE, true);
-
- final RevCommit tip = walk.parseCommit(newChange.getNewId());
- tip.add(SIDE_NEW);
- walk.markStart(tip);
-
- boolean haveHeads = false;
- for (final Ref r : rp.getAdvertisedRefs().values()) {
- if (isHead(r) || isTag(r)) {
- try {
- final RevCommit h = walk.parseCommit(r.getObjectId());
- h.add(SIDE_HAVE);
- walk.markStart(h);
- haveHeads = true;
- } catch (IOException e) {
- continue;
- }
- }
- }
-
- if (haveHeads) {
- boolean isConnected = false;
- RevCommit c;
- while ((c = walk.next()) != null) {
- if (c.hasAll(COMMON)) {
- isConnected = true;
- break;
- }
- }
- if (!isConnected) {
- reject(newChange, "no common ancestry");
- return;
- }
- }
- } catch (IOException e) {
- newChange.setResult(Result.REJECTED_MISSING_OBJECT);
- log.error("Invalid pack upload; one or more objects weren't sent", e);
- return;
- }
- }
-
- private void parseReplaceCommand(final ReceiveCommand cmd,
- final Change.Id changeId) {
- if (cmd.getType() != ReceiveCommand.Type.CREATE) {
- reject(cmd, "invalid usage");
- return;
- }
-
- final RevCommit newCommit;
- try {
- newCommit = rp.getRevWalk().parseCommit(cmd.getNewId());
- } catch (IOException e) {
- log.error("Cannot parse " + cmd.getNewId().name() + " as commit", e);
- reject(cmd, "invalid commit");
- return;
- }
-
- final Change changeEnt;
- try {
- changeEnt = db.changes().get(changeId);
- } catch (OrmException e) {
- log.error("Cannot lookup existing change " + changeId, e);
- reject(cmd, "database error");
- return;
- }
- if (changeEnt == null) {
- reject(cmd, "change " + changeId + " not found");
- return;
- }
- if (!project.getNameKey().equals(changeEnt.getProject())) {
- reject(cmd, "change " + changeId + " not found");
- return;
- }
-
- requestReplace(cmd, changeEnt, newCommit);
- }
-
- private void requestReplace(final ReceiveCommand cmd, final Change change,
- final RevCommit newCommit) {
- if (change.getStatus().isClosed()) {
- reject(cmd, "change " + change.getId() + " closed");
- return;
- }
-
- final ReplaceRequest req =
- new ReplaceRequest(change.getId(), newCommit, cmd);
- if (replaceByChange.containsKey(req.ontoChange)) {
- reject(cmd, "duplicate request");
- return;
- }
- if (replaceByCommit.containsKey(req.newCommit)) {
- reject(cmd, "duplicate request");
- return;
- }
- replaceByChange.put(req.ontoChange, req);
- replaceByCommit.put(req.newCommit, req);
- }
-
- private void createNewChanges() {
- final List<RevCommit> toCreate = new ArrayList<RevCommit>();
- final RevWalk walk = rp.getRevWalk();
- walk.reset();
- walk.sort(RevSort.TOPO);
- walk.sort(RevSort.REVERSE, true);
- try {
- walk.markStart(walk.parseCommit(newChange.getNewId()));
- for (final Ref r : rp.getAdvertisedRefs().values()) {
- try {
- walk.markUninteresting(walk.parseCommit(r.getObjectId()));
- } catch (IOException e) {
- continue;
- }
- }
-
- for (;;) {
- final RevCommit c = walk.next();
- if (c == null) {
- break;
- }
- if (replaceByCommit.containsKey(c)) {
- // This commit was already scheduled to replace an existing PatchSet.
- //
- continue;
- }
- if (!validCommitter(newChange, c)) {
- // Not a change the user can propose? Abort as early as possible.
- //
- return;
- }
-
- final List<String> idList = c.getFooterLines(CHANGE_ID);
- if (!idList.isEmpty()) {
- final Change.Key key = new Change.Key(idList.get(idList.size() - 1));
- final List<Change> changes =
- db.changes().byProjectKey(project.getNameKey(), key).toList();
- if (changes.size() > 1) {
- // WTF, multiple changes in this project have the same key?
- // Since the commit is new, the user should recreate it with
- // a different Change-Id. In practice, we should never see
- // this error message as Change-Id should be unique.
- //
- reject(newChange, key.get() + " has duplicates");
- return;
-
- }
-
- if (changes.size() == 1) {
- // Schedule as a replacement to this one matching change.
- //
- requestReplace(newChange, changes.get(0), c);
- continue;
- }
- }
-
- toCreate.add(c);
- }
- } catch (IOException e) {
- // Should never happen, the core receive process would have
- // identified the missing object earlier before we got control.
- //
- newChange.setResult(Result.REJECTED_MISSING_OBJECT);
- log.error("Invalid pack upload; one or more objects weren't sent", e);
- return;
- } catch (OrmException e) {
- log.error("Cannot query database to locate prior changes", e);
- reject(newChange, "database error");
- return;
- }
-
- if (toCreate.isEmpty() && replaceByChange.isEmpty()) {
- reject(newChange, "no new changes");
- return;
- }
-
- for (final RevCommit c : toCreate) {
- try {
- createChange(walk, c);
- } catch (IOException e) {
- log.error("Error computing patch of commit " + c.name(), e);
- reject(newChange, "diff error");
- return;
- } catch (OrmException e) {
- log.error("Error creating change for commit " + c.name(), e);
- reject(newChange, "database error");
- return;
- }
- }
- newChange.setResult(ReceiveCommand.Result.OK);
- }
-
- private void createChange(final RevWalk walk, final RevCommit c)
- throws OrmException, IOException {
- walk.parseBody(c);
-
- final Account.Id me = currentUser.getAccountId();
- Change.Key changeKey = new Change.Key("I" + c.name());
- final Set<Account.Id> reviewers = new HashSet<Account.Id>(reviewerId);
- final Set<Account.Id> cc = new HashSet<Account.Id>(ccId);
- for (final FooterLine footerLine : c.getFooterLines()) {
- try {
- if (footerLine.matches(CHANGE_ID)) {
- final String v = footerLine.getValue().trim();
- if (v.matches("^I[0-9a-f]{8,}.*$")) {
- changeKey = new Change.Key(v);
- }
- } else if (isReviewer(footerLine)) {
- reviewers.add(toAccountId(footerLine.getValue().trim()));
- } else if (footerLine.matches(FooterKey.CC)) {
- cc.add(toAccountId(footerLine.getValue().trim()));
- }
- } catch (NoSuchAccountException e) {
- continue;
- }
- }
- reviewers.remove(me);
- cc.remove(me);
- cc.removeAll(reviewers);
-
- final Transaction txn = db.beginTransaction();
- final Change change =
- new Change(changeKey, new Change.Id(db.nextChangeId()), me, destBranch);
- final PatchSet ps = new PatchSet(change.newPatchSetId());
- ps.setCreatedOn(change.getCreatedOn());
- ps.setUploader(me);
-
- final PatchSetImporter imp = importFactory.create(db, c, ps, true);
- imp.setTransaction(txn);
- imp.run();
-
- change.setCurrentPatchSet(imp.getPatchSetInfo());
- ChangeUtil.updated(change);
- db.changes().insert(Collections.singleton(change), txn);
-
- final Set<Account.Id> haveApprovals = new HashSet<Account.Id>();
- final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
- haveApprovals.add(me);
-
- if (allTypes.size() > 0) {
- final Account.Id authorId =
- imp.getPatchSetInfo().getAuthor() != null ? imp.getPatchSetInfo()
- .getAuthor().getAccount() : null;
- final Account.Id committerId =
- imp.getPatchSetInfo().getCommitter() != null ? imp.getPatchSetInfo()
- .getCommitter().getAccount() : null;
- final ApprovalCategory.Id catId =
- allTypes.get(allTypes.size() - 1).getCategory().getId();
- if (authorId != null && haveApprovals.add(authorId)) {
- insertDummyApproval(change, ps.getId(), authorId, catId, db, txn);
- }
- if (committerId != null && haveApprovals.add(committerId)) {
- insertDummyApproval(change, ps.getId(), committerId, catId, db, txn);
- }
- for (final Account.Id reviewer : reviewers) {
- if (haveApprovals.add(reviewer)) {
- insertDummyApproval(change, ps.getId(), reviewer, catId, db, txn);
- }
- }
- }
-
- txn.commit();
-
- final RefUpdate ru = repo.updateRef(ps.getRefName());
- ru.setNewObjectId(c);
- ru.disableRefLog();
- if (ru.update(walk) != RefUpdate.Result.NEW) {
- throw new IOException("Failed to create ref " + ps.getRefName() + " in "
- + repo.getDirectory() + ": " + ru.getResult());
- }
- replication.scheduleUpdate(project.getNameKey(), ru.getName());
-
- allNewChanges.add(change.getId());
-
- try {
- final CreateChangeSender cm;
- cm = createChangeSenderFactory.create(change);
- cm.setFrom(me);
- cm.setPatchSet(ps, imp.getPatchSetInfo());
- cm.setReviewDb(db);
- cm.addReviewers(reviewers);
- cm.addExtraCC(cc);
- cm.send();
- } catch (EmailException e) {
- log.error("Cannot send email for new change " + change.getId(), e);
- }
- }
-
- private static boolean isReviewer(final FooterLine candidateFooterLine) {
- return candidateFooterLine.matches(FooterKey.SIGNED_OFF_BY)
- || candidateFooterLine.matches(FooterKey.ACKED_BY)
- || candidateFooterLine.matches(REVIEWED_BY)
- || candidateFooterLine.matches(TESTED_BY);
- }
-
- private void doReplaces() {
- for (final ReplaceRequest request : replaceByChange.values()) {
- try {
- doReplace(request);
- } catch (IOException err) {
- log.error("Error computing replacement patch for change "
- + request.ontoChange + ", commit " + request.newCommit.name(), err);
- reject(request.cmd, "diff error");
- } catch (OrmException err) {
- log.error("Error storing replacement patch for change "
- + request.ontoChange + ", commit " + request.newCommit.name(), err);
- reject(request.cmd, "database error");
- }
- if (request.cmd.getResult() == ReceiveCommand.Result.NOT_ATTEMPTED) {
- log.error("Replacement patch for change " + request.ontoChange
- + ", commit " + request.newCommit.name() + " wasn't attempted."
- + " This is a bug in the receive process implementation.");
- reject(request.cmd, "internal error");
- }
- }
- }
-
- private PatchSet.Id doReplace(final ReplaceRequest request)
- throws IOException, OrmException {
- final RevCommit c = request.newCommit;
- rp.getRevWalk().parseBody(c);
- if (!validCommitter(request.cmd, c)) {
- return null;
- }
-
- final Account.Id me = currentUser.getAccountId();
- final Set<Account.Id> reviewers = new HashSet<Account.Id>(reviewerId);
- final Set<Account.Id> cc = new HashSet<Account.Id>(ccId);
- for (final FooterLine footerLine : c.getFooterLines()) {
- try {
- if (isReviewer(footerLine)) {
- reviewers.add(toAccountId(footerLine.getValue().trim()));
- } else if (footerLine.matches(FooterKey.CC)) {
- cc.add(toAccountId(footerLine.getValue().trim()));
- }
- } catch (NoSuchAccountException e) {
- continue;
- }
- }
- reviewers.remove(me);
- cc.remove(me);
- cc.removeAll(reviewers);
-
- final ReplaceResult result;
-
- final Set<Account.Id> oldReviewers = new HashSet<Account.Id>();
- final Set<Account.Id> oldCC = new HashSet<Account.Id>();
-
- result = db.run(new OrmRunnable<ReplaceResult, ReviewDb>() {
- public ReplaceResult run(final ReviewDb db, final Transaction txn,
- final boolean isRetry) throws OrmException {
- final Change change = db.changes().get(request.ontoChange);
- if (change == null) {
- reject(request.cmd, "change " + request.ontoChange + " not found");
- return null;
- }
- if (change.getStatus().isClosed()) {
- reject(request.cmd, "change " + request.ontoChange + " closed");
- return null;
- }
-
- final PatchSet.Id priorPatchSet = change.currentPatchSetId();
- for (final PatchSet ps : db.patchSets().byChange(request.ontoChange)) {
- if (ps.getRevision() == null) {
- reject(request.cmd, "change state corrupt");
- return null;
- }
-
- final String revIdStr = ps.getRevision().get();
- final ObjectId commitId;
- try {
- commitId = ObjectId.fromString(revIdStr);
- } catch (IllegalArgumentException e) {
- log.warn("Invalid revision in " + ps.getId() + ": " + revIdStr);
- reject(request.cmd, "change state corrupt");
- return null;
- }
-
- try {
- final RevCommit prior = rp.getRevWalk().parseCommit(commitId);
-
- // Don't allow a change to directly depend upon itself. This is a
- // very common error due to users making a new commit rather than
- // amending when trying to address review comments.
- //
- if (rp.getRevWalk().isMergedInto(prior, c)) {
- reject(request.cmd, "squash commits first");
- return null;
- }
-
- // Don't allow the same commit to appear twice on the same change
- //
- if (c == prior) {
- reject(request.cmd, "commit already exists");
- return null;
- }
-
- // Don't allow the same tree if the commit message is unmodified,
- // else warn that only the message changed.
- //
- if (priorPatchSet.equals(ps.getId())
- && c.getTree() == prior.getTree()) {
- rp.getRevWalk().parseBody(prior);
- if (c.getFullMessage().equals(prior.getFullMessage())) {
- reject(request.cmd, "no changes made");
- return null;
- } else {
- err.write(Constants
- .encode("warning: Only commit message changed in "
- + change.getKey().abbreviate() + "\n"));
- }
- }
- } catch (IOException e) {
- log.error("Change " + change.getId() + " missing " + revIdStr, e);
- reject(request.cmd, "change state corrupt");
- return null;
- }
- }
-
- final PatchSet ps = new PatchSet(change.newPatchSetId());
- ps.setCreatedOn(new Timestamp(System.currentTimeMillis()));
- ps.setUploader(currentUser.getAccountId());
-
- final PatchSetImporter imp = importFactory.create(db, c, ps, true);
- imp.setTransaction(txn);
- imp.run();
-
- final Ref mergedInto = findMergedInto(change.getDest().get(), c);
- final ReplaceResult result = new ReplaceResult();
- result.mergedIntoRef = mergedInto != null ? mergedInto.getName() : null;
- result.change = change;
- result.patchSet = ps;
- result.info = imp.getPatchSetInfo();
-
- final Account.Id authorId =
- imp.getPatchSetInfo().getAuthor() != null ? imp.getPatchSetInfo()
- .getAuthor().getAccount() : null;
- final Account.Id committerId =
- imp.getPatchSetInfo().getCommitter() != null ? imp
- .getPatchSetInfo().getCommitter().getAccount() : null;
-
- boolean haveAuthor = false;
- boolean haveCommitter = false;
- final Set<Account.Id> haveApprovals = new HashSet<Account.Id>();
-
- oldReviewers.clear();
- oldCC.clear();
-
- for (PatchSetApproval a : db.patchSetApprovals().byChange(
- change.getId())) {
- haveApprovals.add(a.getAccountId());
-
- if (a.getValue() != 0) {
- oldReviewers.add(a.getAccountId());
- } else {
- oldCC.add(a.getAccountId());
- }
-
- final ApprovalType type =
- approvalTypes.getApprovalType(a.getCategoryId());
- if (a.getPatchSetId().equals(priorPatchSet)
- && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
- // If there was a negative vote on the prior patch set, carry it
- // into this patch set.
- //
- db.patchSetApprovals()
- .insert(
- Collections.singleton(new PatchSetApproval(ps.getId(), a)),
- txn);
- }
-
- if (!haveAuthor && authorId != null
- && a.getAccountId().equals(authorId)) {
- haveAuthor = true;
- }
- if (!haveCommitter && committerId != null
- && a.getAccountId().equals(committerId)) {
- haveCommitter = true;
- }
- }
-
- final ChangeMessage msg =
- new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
- .messageUUID(db)), me, ps.getCreatedOn());
- msg.setMessage("Uploaded patch set " + ps.getPatchSetId() + ".");
- db.changeMessages().insert(Collections.singleton(msg), txn);
- result.msg = msg;
-
- if (result.mergedIntoRef != null) {
- // Change was already submitted to a branch, close it.
- //
- markChangeMergedByPush(db, txn, result);
- } else {
- // Change should be new, so it can go through review again.
- //
- change.setStatus(Change.Status.NEW);
- change.setCurrentPatchSet(imp.getPatchSetInfo());
- ChangeUtil.updated(change);
- db.changes().update(Collections.singleton(change), txn);
- }
-
- final List<ApprovalType> allTypes = approvalTypes.getApprovalTypes();
- if (allTypes.size() > 0) {
- final ApprovalCategory.Id catId =
- allTypes.get(allTypes.size() - 1).getCategory().getId();
- if (authorId != null && haveApprovals.add(authorId)) {
- insertDummyApproval(result, authorId, catId, db, txn);
- }
- if (committerId != null && haveApprovals.add(committerId)) {
- insertDummyApproval(result, committerId, catId, db, txn);
- }
- for (final Account.Id reviewer : reviewers) {
- if (haveApprovals.add(reviewer)) {
- insertDummyApproval(result, reviewer, catId, db, txn);
- }
- }
- }
- return result;
- }
- });
- if (result != null) {
- final PatchSet ps = result.patchSet;
- final RefUpdate ru = repo.updateRef(ps.getRefName());
- ru.setNewObjectId(c);
- ru.disableRefLog();
- if (ru.update(rp.getRevWalk()) != RefUpdate.Result.NEW) {
- throw new IOException("Failed to create ref " + ps.getRefName()
- + " in " + repo.getDirectory() + ": " + ru.getResult());
- }
- replication.scheduleUpdate(project.getNameKey(), ru.getName());
- request.cmd.setResult(ReceiveCommand.Result.OK);
-
- try {
- final ReplacePatchSetSender cm;
- cm = replacePatchSetFactory.create(result.change);
- cm.setFrom(me);
- cm.setPatchSet(ps, result.info);
- cm.setChangeMessage(result.msg);
- cm.setReviewDb(db);
- cm.addReviewers(reviewers);
- cm.addExtraCC(cc);
- cm.addReviewers(oldReviewers);
- cm.addExtraCC(oldCC);
- cm.send();
- } catch (EmailException e) {
- log.error("Cannot send email for new patch set " + ps.getId(), e);
- }
- }
- sendMergedEmail(result);
- return result != null ? result.info.getKey() : null;
- }
-
- private void insertDummyApproval(final ReplaceResult result,
- final Account.Id forAccount, final ApprovalCategory.Id catId,
- final ReviewDb db, final Transaction txn) throws OrmException {
- insertDummyApproval(result.change, result.patchSet.getId(), forAccount,
- catId, db, txn);
- }
-
- private void insertDummyApproval(final Change change, final PatchSet.Id psId,
- final Account.Id forAccount, final ApprovalCategory.Id catId,
- final ReviewDb db, final Transaction txn) throws OrmException {
- final PatchSetApproval ca =
- new PatchSetApproval(new PatchSetApproval.Key(psId, forAccount, catId),
- (short) 0);
- ca.cache(change);
- db.patchSetApprovals().insert(Collections.singleton(ca), txn);
- }
-
- private Ref findMergedInto(final String first, final RevCommit commit) {
- try {
- final Map<String, Ref> all = repo.getAllRefs();
- Ref firstRef = all.get(first);
- if (firstRef != null && isMergedInto(commit, firstRef)) {
- return firstRef;
- }
- for (Ref ref : all.values()) {
- if (isHead(ref)) {
- if (isMergedInto(commit, ref)) {
- return ref;
- }
- }
- }
- return null;
- } catch (IOException e) {
- log.warn("Can't check for already submitted change", e);
- return null;
- }
- }
-
- private boolean isMergedInto(final RevCommit commit, final Ref ref)
- throws IOException {
- final RevWalk rw = rp.getRevWalk();
- return rw.isMergedInto(commit, rw.parseCommit(ref.getObjectId()));
- }
-
- private static class ReplaceRequest {
- final Change.Id ontoChange;
- final RevCommit newCommit;
- final ReceiveCommand cmd;
-
- ReplaceRequest(final Change.Id toChange, final RevCommit newCommit,
- final ReceiveCommand cmd) {
- this.ontoChange = toChange;
- this.newCommit = newCommit;
- this.cmd = cmd;
- }
- }
-
- private static class ReplaceResult {
- Change change;
- PatchSet patchSet;
- PatchSetInfo info;
- ChangeMessage msg;
- String mergedIntoRef;
- }
-
- private boolean validCommitter(final ReceiveCommand cmd, final RevCommit c)
- throws MissingObjectException, IOException {
- rp.getRevWalk().parseBody(c);
- final PersonIdent committer = c.getCommitterIdent();
- final PersonIdent author = c.getAuthorIdent();
-
- // Don't allow the user to amend a merge created by Gerrit Code Review.
- // This seems to happen all too often, due to users not paying any
- // attention to what they are doing.
- //
- if (c.getParentCount() > 1
- && author.getName().equals(gerritIdent.getName())
- && author.getEmailAddress().equals(gerritIdent.getEmailAddress())) {
- reject(cmd, "do not amend merges not made by you");
- return false;
- }
-
- // Require that committer matches the uploader.
- //
- if (!currentUser.getEmailAddresses().contains(committer.getEmailAddress())) {
- reject(cmd, "you are not committer " + committer.getEmailAddress());
- return false;
- }
-
- if (project.isUseSignedOffBy()) {
- // If the project wants Signed-off-by / Acked-by lines, verify we
- // have them for the blamable parties involved on this change.
- //
- boolean sboAuthor = false, sboCommitter = false, sboMe = false;
- for (final FooterLine footer : c.getFooterLines()) {
- if (footer.matches(FooterKey.SIGNED_OFF_BY)) {
- final String e = footer.getEmailAddress();
- if (e != null) {
- sboAuthor |= author.getEmailAddress().equals(e);
- sboCommitter |= committer.getEmailAddress().equals(e);
- sboMe |= currentUser.getEmailAddresses().contains(e);
- }
- }
- }
- if (!sboAuthor && !sboCommitter && !sboMe) {
- reject(cmd, "not Signed-off-by author/committer/uploader");
- return false;
- }
- }
-
- return true;
- }
-
- private void autoCloseChanges(final ReceiveCommand cmd) {
- final RevWalk rw = rp.getRevWalk();
- try {
- rw.reset();
- rw.markStart(rw.parseCommit(cmd.getNewId()));
- if (!ObjectId.zeroId().equals(cmd.getOldId())) {
- rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
- }
-
- final Map<ObjectId, Ref> byCommit = changeRefsById();
- final Map<Change.Key, Change.Id> byKey = openChangesByKey();
- final List<ReplaceRequest> toClose = new ArrayList<ReplaceRequest>();
- RevCommit c;
- while ((c = rw.next()) != null) {
- final Ref ref = byCommit.get(c.copy());
- if (ref != null) {
- closeChange(cmd, PatchSet.Id.fromRef(ref.getName()), c);
- continue;
- }
-
- rw.parseBody(c);
- for (final String changeId : c.getFooterLines(CHANGE_ID)) {
- final Change.Id onto = byKey.get(new Change.Key(changeId));
- if (onto != null) {
- toClose.add(new ReplaceRequest(onto, c, cmd));
- break;
- }
- }
- }
-
- for (final ReplaceRequest req : toClose) {
- final PatchSet.Id psi = doReplace(req);
- if (psi != null) {
- closeChange(req.cmd, psi, req.newCommit);
- } else {
- log.warn("Replacement of Change-Id " + req.ontoChange
- + " with commit " + req.newCommit.name()
- + " did not import the new patch set.");
- }
- }
- } catch (IOException e) {
- log.error("Can't scan for changes to close", e);
- } catch (OrmException e) {
- log.error("Can't scan for changes to close", e);
- }
- }
-
- private void closeChange(final ReceiveCommand cmd, final PatchSet.Id psi,
- final RevCommit commit) throws OrmException {
- final String refName = cmd.getRefName();
- final Change.Id cid = psi.getParentKey();
- final ReplaceResult result =
- db.run(new OrmRunnable<ReplaceResult, ReviewDb>() {
- @Override
- public ReplaceResult run(ReviewDb db, Transaction txn, boolean retry)
- throws OrmException {
- final Change change = db.changes().get(cid);
- final PatchSet ps = db.patchSets().get(psi);
- if (change == null || ps == null) {
- log.warn(project.getName() + " " + psi + " is missing");
- return null;
- }
-
- if (change.getStatus() == Change.Status.MERGED) {
- // If its already merged, don't make further updates, it
- // might just be moving from an experimental branch into
- // a more stable branch.
- //
- return null;
- }
-
- final ReplaceResult result = new ReplaceResult();
- result.change = change;
- result.patchSet = ps;
- result.info = patchSetInfoFactory.get(commit, psi);
- result.mergedIntoRef = refName;
- markChangeMergedByPush(db, txn, result);
- return result;
- }
- });
- sendMergedEmail(result);
- }
-
- private Map<ObjectId, Ref> changeRefsById() {
- if (refsById == null) {
- refsById = new HashMap<ObjectId, Ref>();
- for (final Ref r : repo.getAllRefs().values()) {
- if (PatchSet.isRef(r.getName())) {
- refsById.put(r.getObjectId(), r);
- }
- }
- }
- return refsById;
- }
-
- private Map<Change.Key, Change.Id> openChangesByKey() throws OrmException {
- final Map<Change.Key, Change.Id> r = new HashMap<Change.Key, Change.Id>();
- for (Change c : db.changes().byProjectOpenAll(project.getNameKey())) {
- r.put(c.getKey(), c.getId());
- }
- return r;
- }
-
- private void markChangeMergedByPush(final ReviewDb db, final Transaction txn,
- final ReplaceResult result) throws OrmException {
- final Change change = result.change;
- final String mergedIntoRef = result.mergedIntoRef;
-
- change.setCurrentPatchSet(result.info);
- change.setStatus(Change.Status.MERGED);
- ChangeUtil.updated(change);
-
- final List<PatchSetApproval> approvals =
- db.patchSetApprovals().byChange(change.getId()).toList();
- for (PatchSetApproval a : approvals) {
- a.cache(change);
- }
-
- final StringBuilder msgBuf = new StringBuilder();
- msgBuf.append("Change has been successfully pushed");
- if (!mergedIntoRef.equals(change.getDest().get())) {
- msgBuf.append(" into ");
- if (mergedIntoRef.startsWith(Constants.R_HEADS)) {
- msgBuf.append("branch ");
- msgBuf.append(repo.shortenRefName(mergedIntoRef));
- } else {
- msgBuf.append(mergedIntoRef);
- }
- }
- msgBuf.append(".");
- final ChangeMessage msg =
- new ChangeMessage(new ChangeMessage.Key(change.getId(), ChangeUtil
- .messageUUID(db)), currentUser.getAccountId());
- msg.setMessage(msgBuf.toString());
-
- db.patchSetApprovals().update(approvals, txn);
- db.changeMessages().insert(Collections.singleton(msg), txn);
- db.changes().update(Collections.singleton(change), txn);
- }
-
- private void sendMergedEmail(final ReplaceResult result) {
- if (result != null && result.mergedIntoRef != null) {
- try {
- final MergedSender cm = mergedSenderFactory.create(result.change);
- cm.setFrom(currentUser.getAccountId());
- cm.setReviewDb(db);
- cm.setPatchSet(result.patchSet, result.info);
- cm.setDest(new Branch.NameKey(project.getNameKey(),
- result.mergedIntoRef));
- cm.send();
- } catch (EmailException e) {
- final PatchSet.Id psi = result.patchSet.getId();
- log.error("Cannot send email for submitted patch set " + psi, e);
- }
- }
- }
-
- private boolean canPerform(final ApprovalCategory.Id actionId, final short val) {
- return projectControl.canPerform(actionId, val);
- }
-
- private static void reject(final ReceiveCommand cmd) {
- reject(cmd, "prohibited by Gerrit");
- }
-
- private static void reject(final ReceiveCommand cmd, final String why) {
- cmd.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, why);
- }
-
- private static boolean isTag(final Ref ref) {
- return ref.getName().startsWith(Constants.R_TAGS);
- }
-
- private static boolean isTag(final ReceiveCommand cmd) {
- return cmd.getRefName().startsWith(Constants.R_TAGS);
- }
-
- private static boolean isHead(final Ref ref) {
- return ref.getName().startsWith(Constants.R_HEADS);
- }
-
- private static boolean isHead(final ReceiveCommand cmd) {
- return cmd.getRefName().startsWith(Constants.R_HEADS);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/ScpCommand.java b/src/main/java/com/google/gerrit/server/ssh/commands/ScpCommand.java
deleted file mode 100644
index c31ea4219d..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/ScpCommand.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to you 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.
- */
-
-/*
- * NB: This code was primarly ripped out of MINA SSHD.
- *
- * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
- */
-package com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.pgm.Version;
-import com.google.gerrit.server.ssh.BaseCommand;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.eclipse.jgit.util.RawParseUtils;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.TreeMap;
-
-final class ScpCommand extends BaseCommand {
- private static final String TYPE_DIR = "D";
- private static final String TYPE_FILE = "C";
- private static final Logger log = LoggerFactory.getLogger(ScpCommand.class);
-
- private boolean opt_r;
- private boolean opt_t;
- private boolean opt_f;
- private boolean opt_v;
- private boolean opt_p;
- private String root;
-
- private TreeMap<String, Entry> toc;
- private IOException error;
-
- @Override
- public void setCommandLine(final String line) {
- super.setCommandLine(line);
-
- final String[] args = line.split(" ");
- root = "";
- for (int i = 0; i < args.length; i++) {
- if (args[i].charAt(0) == '-') {
- for (int j = 1; j < args[i].length(); j++) {
- switch (args[i].charAt(j)) {
- case 'f':
- opt_f = true;
- break;
- case 'p':
- opt_p = true;
- break;
- case 'r':
- opt_r = true;
- break;
- case 't':
- opt_t = true;
- break;
- case 'v':
- opt_v = true;
- break;
- }
- }
- } else if (i == args.length - 1) {
- root = args[args.length - 1];
- }
- }
- if (!opt_f && !opt_t) {
- error = new IOException("Either -f or -t option should be set");
- }
- }
-
- @Override
- public void start() {
- startThread(new Runnable() {
- public void run() {
- runImp();
- }
- });
- }
-
- private void runImp() {
- try {
- if (error != null) {
- throw error;
- }
-
- readToc();
- if (opt_f) {
- if (root.startsWith("/")) {
- root = root.substring(1);
- }
- if (root.endsWith("/")) {
- root = root.substring(0, root.length() - 1);
- }
- if (root.equals(".")) {
- root = "";
- }
-
- final Entry ent = toc.get(root);
- if (ent == null) {
- throw new IOException(root + " not found");
-
- } else if (TYPE_FILE.equals(ent.type)) {
- readFile(ent);
-
- } else if (TYPE_DIR.equals(ent.type)) {
- if (!opt_r) {
- throw new IOException(root + " not a regular file");
- }
- readDir(ent);
- } else {
- throw new IOException(root + " not supported");
- }
- } else {
- throw new IOException("Unsupported mode");
- }
- } catch (IOException e) {
- if (e.getClass() == IOException.class
- && "Pipe closed".equals(e.getMessage())) {
- // Ignore a pipe closed error, its the user disconnecting from us
- // while we are waiting for them to stalk.
- //
- return;
- }
-
- try {
- out.write(2);
- out.write(e.getMessage().getBytes());
- out.write('\n');
- out.flush();
- } catch (IOException e2) {
- // Ignore
- }
- log.debug("Error in scp command", e);
- }
- }
-
- private void readToc() throws IOException {
- toc = new TreeMap<String, Entry>();
- final BufferedReader br =
- new BufferedReader(new InputStreamReader(new ByteArrayInputStream(
- read("TOC")), "UTF-8"));
- String line;
- while ((line = br.readLine()) != null) {
- if (line.length() > 0 && !line.startsWith("#")) {
- final Entry e = new Entry(TYPE_FILE, line);
- toc.put(e.path, e);
- }
- }
-
- final List<Entry> all = new ArrayList<Entry>(toc.values());
- for (Entry e : all) {
- String path = dirOf(e.path);
- while (path != null) {
- Entry d = toc.get(path);
- if (d == null) {
- d = new Entry(TYPE_DIR, 0755, path);
- toc.put(d.path, d);
- }
- d.children.add(e);
- path = dirOf(path);
- e = d;
- }
- }
-
- final Entry top = new Entry(TYPE_DIR, 0755, "");
- for (Entry e : toc.values()) {
- if (dirOf(e.path) == null) {
- top.children.add(e);
- }
- }
- toc.put(top.path, top);
- }
-
- private String readLine() throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for (;;) {
- int c = in.read();
- if (c == '\n') {
- return baos.toString();
- } else if (c == -1) {
- throw new IOException("End of stream");
- } else {
- baos.write(c);
- }
- }
- }
-
- private static String nameOf(String path) {
- final int s = path.lastIndexOf('/');
- return s < 0 ? path : path.substring(s + 1);
- }
-
- private static String dirOf(String path) {
- final int s = path.lastIndexOf('/');
- return s < 0 ? null : path.substring(0, s);
- }
-
- private static byte[] read(String path) {
- final InputStream in =
- ScpCommand.class.getClassLoader().getResourceAsStream(
- "com/google/gerrit/server/ssh/scproot/" + path);
- if (in == null) {
- return null;
- }
- try {
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- try {
- final byte[] buf = new byte[8192];
- int n;
- while ((n = in.read(buf, 0, buf.length)) > 0) {
- out.write(buf, 0, n);
- }
- } finally {
- in.close();
- }
- return out.toByteArray();
- } catch (Exception e) {
- log.debug("Cannot read " + path, e);
- return null;
- }
- }
-
- private void readFile(final Entry ent) throws IOException {
- byte[] data = read(ent.path);
- if (data == null) {
- throw new FileNotFoundException(ent.path);
- }
-
- if (data.length > 3 && data[0] == '#' && data[1] == '!' && data[2] == '/') {
- // Embed Gerrit's version number into the top of the script.
- //
- final String version = Version.getVersion();
- final int lf = RawParseUtils.nextLF(data, 0);
- if (version != null && lf < data.length) {
- final byte[] versionHeader =
- ("# From Gerrit Code Review " + version + "\n").getBytes("UTF-8");
- final ByteArrayOutputStream buf;
- buf = new ByteArrayOutputStream(data.length + versionHeader.length);
- buf.write(data, 0, lf);
- buf.write(versionHeader);
- buf.write(data, lf, data.length - lf);
- data = buf.toByteArray();
- }
- }
-
- header(ent, data.length);
- readAck();
-
- out.write(data);
- ack();
- readAck();
- }
-
- private void readDir(final Entry dir) throws IOException {
- header(dir, 0);
- readAck();
-
- for (Entry e : dir.children) {
- if (TYPE_DIR.equals(e.type)) {
- readDir(e);
- } else {
- readFile(e);
- }
- }
-
- out.write("E\n".getBytes("UTF-8"));
- out.flush();
- readAck();
- }
-
- private void header(final Entry dir, final int len) throws IOException,
- UnsupportedEncodingException {
- final StringBuilder buf = new StringBuilder();
- buf.append(dir.type);
- buf.append(dir.mode); // perms
- buf.append(" ");
- buf.append(len); // length
- buf.append(" ");
- buf.append(nameOf(dir.path));
- buf.append("\n");
- out.write(buf.toString().getBytes("UTF-8"));
- out.flush();
- }
-
- private void ack() throws IOException {
- out.write(0);
- out.flush();
- }
-
- private void readAck() throws IOException {
- switch (in.read()) {
- case 0:
- break;
- case 1:
- log.debug("Received warning: " + readLine());
- break;
- case 2:
- throw new IOException("Received nack: " + readLine());
- }
- }
-
- private static class Entry {
- String type;
- String mode;
- String path;
- List<Entry> children;
-
- Entry(String type, String line) {
- this.type = type;
- int s = line.indexOf(' ');
- mode = line.substring(0, s);
- path = line.substring(s + 1);
-
- if (!mode.startsWith("0")) {
- mode = "0" + mode;
- }
- }
-
- Entry(String type, int mode, String path) {
- this.type = type;
- this.mode = "0" + Integer.toOctalString(mode);
- this.path = path;
- this.children = new ArrayList<Entry>();
- }
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/SlaveCommandModule.java b/src/main/java/com/google/gerrit/server/ssh/commands/SlaveCommandModule.java
deleted file mode 100644
index 449de813f9..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/SlaveCommandModule.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import com.google.gerrit.server.ssh.CommandModule;
-import com.google.gerrit.server.ssh.CommandName;
-import com.google.gerrit.server.ssh.Commands;
-
-
-/** Register the commands a Gerrit server in slave mode supports. */
-public class SlaveCommandModule extends CommandModule {
- @Override
- protected void configure() {
- final CommandName gerrit = Commands.named("gerrit");
-
- command(gerrit, "approve").to(ErrorSlaveMode.class);
- command(gerrit, "create-project").to(ErrorSlaveMode.class);
- command(gerrit, "receive-pack").to(ErrorSlaveMode.class);
- command(gerrit, "replicate").to(ErrorSlaveMode.class);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/ssh/commands/Upload.java b/src/main/java/com/google/gerrit/server/ssh/commands/Upload.java
deleted file mode 100644
index 6e03829901..0000000000
--- a/src/main/java/com/google/gerrit/server/ssh/commands/Upload.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.commands;
-
-import org.eclipse.jgit.transport.UploadPack;
-
-import java.io.IOException;
-
-/** Publishes Git repositories over SSH using the Git upload-pack protocol. */
-final class Upload extends AbstractGitCommand {
- @Override
- protected void runImpl() throws IOException {
- final UploadPack up = new UploadPack(repo);
- up.upload(in, out, err);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java b/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
deleted file mode 100644
index b92ac573c1..0000000000
--- a/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.workflow;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.server.CurrentUser;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/** Function to control {@link PatchSetApproval}s in an {@link ApprovalCategory}. */
-public abstract class CategoryFunction {
- private static Map<String, CategoryFunction> all =
- new HashMap<String, CategoryFunction>();
- static {
- all.put(SubmitFunction.NAME, new SubmitFunction());
- all.put(MaxWithBlock.NAME, new MaxWithBlock());
- all.put(NoOpFunction.NAME, new NoOpFunction());
- }
-
- /**
- * Locate a function by category.
- *
- * @param category the category the function is for.
- * @return the function implementation; {@link NoOpFunction} if the function
- * is not known to Gerrit and thus cannot be executed.
- */
- public static CategoryFunction forCategory(final ApprovalCategory category) {
- final CategoryFunction r = forName(category.getFunctionName());
- return r != null ? r : new NoOpFunction();
- }
-
- /**
- * Locate a function by name.
- *
- * @param functionName the function's unique name.
- * @return the function implementation; null if the function is not known to
- * Gerrit and thus cannot be executed.
- */
- public static CategoryFunction forName(final String functionName) {
- return all.get(functionName);
- }
-
- /**
- * Normalize ChangeApprovals and set the valid flag for this category.
- * <p>
- * Implementors should invoke:
- *
- * <pre>
- * state.valid(at, true);
- * </pre>
- * <p>
- * If the set of approvals from <code>state.getApprovals(at)</code> covers the
- * requirements for the function, indicating the category has been completed.
- * <p>
- * An example implementation which requires at least one positive and no
- * negatives might be:
- *
- * <pre>
- * boolean neg = false, pos = false;
- * for (final ChangeApproval ca : state.getApprovals(at)) {
- * state.normalize(ca);
- * neg |= ca.getValue() &lt; 0;
- * pos |= ca.getValue() &gt; 0;
- * }
- * state.valid(at, !neg &amp;&amp; pos);
- * </pre>
- *
- * @param at the cached category description to process.
- * @param state state to read approvals and project rights from, and to update
- * the valid status into.
- */
- public abstract void run(ApprovalType at, FunctionState state);
-
- public boolean isValid(final CurrentUser user, final ApprovalType at,
- final FunctionState state) {
- for (final ProjectRight pr : state.getAllRights(at)) {
- if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
- && (pr.getMinValue() < 0 || pr.getMaxValue() > 0)) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
deleted file mode 100644
index ed8cb9b353..0000000000
--- a/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.workflow;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.data.ApprovalTypes;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.PatchSet;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ApprovalCategory.Id;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** State passed through to a {@link CategoryFunction}. */
-public class FunctionState {
- public interface Factory {
- FunctionState create(Change c, PatchSet.Id psId,
- Collection<PatchSetApproval> all);
- }
-
- private final ApprovalTypes approvalTypes;
- private final IdentifiedUser.GenericFactory userFactory;
-
- private final Map<ApprovalCategory.Id, Collection<PatchSetApproval>> approvals =
- new HashMap<ApprovalCategory.Id, Collection<PatchSetApproval>>();
- private final Map<ApprovalCategory.Id, Boolean> valid =
- new HashMap<ApprovalCategory.Id, Boolean>();
- private final Change change;
- private final ProjectState project;
- private final Map<ApprovalCategory.Id, Collection<ProjectRight>> allRights =
- new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>();
- private Map<ApprovalCategory.Id, Collection<ProjectRight>> projectRights;
- private Map<ApprovalCategory.Id, Collection<ProjectRight>> inheritedRights;
- private Set<PatchSetApproval> modified;
-
- @Inject
- FunctionState(final ApprovalTypes approvalTypes,
- final ProjectCache projectCache,
- final IdentifiedUser.GenericFactory userFactory, final GroupCache egc,
- @Assisted final Change c, @Assisted final PatchSet.Id psId,
- @Assisted final Collection<PatchSetApproval> all) {
- this.approvalTypes = approvalTypes;
- this.userFactory = userFactory;
-
- change = c;
- project = projectCache.get(change.getProject());
-
- for (final PatchSetApproval ca : all) {
- if (psId.equals(ca.getPatchSetId())) {
- Collection<PatchSetApproval> l = approvals.get(ca.getCategoryId());
- if (l == null) {
- l = new ArrayList<PatchSetApproval>();
- approvals.put(ca.getCategoryId(), l);
- }
- l.add(ca);
- }
- }
- }
-
- List<ApprovalType> getApprovalTypes() {
- return approvalTypes.getApprovalTypes();
- }
-
- public Change getChange() {
- return change;
- }
-
- public Project getProject() {
- return project.getProject();
- }
-
- public void valid(final ApprovalType at, final boolean v) {
- valid.put(id(at), v);
- }
-
- public boolean isValid(final ApprovalType at) {
- return isValid(id(at));
- }
-
- public boolean isValid(final ApprovalCategory.Id id) {
- final Boolean b = valid.get(id);
- return b != null && b;
- }
-
- public Collection<PatchSetApproval> getApprovals(final ApprovalType at) {
- return getApprovals(id(at));
- }
-
- public Collection<PatchSetApproval> getApprovals(final ApprovalCategory.Id id) {
- final Collection<PatchSetApproval> l = approvals.get(id);
- return l != null ? l : Collections.<PatchSetApproval> emptySet();
- }
-
- public void dirty(final PatchSetApproval ap) {
- if (modified == null) {
- modified = new HashSet<PatchSetApproval>();
- }
- modified.add(ap);
- }
-
- public Collection<PatchSetApproval> getDirtyChangeApprovals() {
- if (modified != null) {
- return modified;
- }
- return Collections.emptySet();
- }
-
- public Collection<ProjectRight> getProjectRights(final ApprovalType at) {
- return getProjectRights(id(at));
- }
-
- public Collection<ProjectRight> getProjectRights(final ApprovalCategory.Id id) {
- if (projectRights == null) {
- projectRights = index(project.getLocalRights());
- }
- final Collection<ProjectRight> l = projectRights.get(id);
- return l != null ? l : Collections.<ProjectRight> emptySet();
- }
-
- public Collection<ProjectRight> getWildcardRights(final ApprovalType at) {
- return getWildcardRights(id(at));
- }
-
- public Collection<ProjectRight> getWildcardRights(final ApprovalCategory.Id id) {
- if (inheritedRights == null) {
- inheritedRights = index(project.getInheritedRights());
- }
- final Collection<ProjectRight> l = inheritedRights.get(id);
- return l != null ? l : Collections.<ProjectRight> emptySet();
- }
-
- public Collection<ProjectRight> getAllRights(final ApprovalType at) {
- return getAllRights(id(at));
- }
-
- public Collection<ProjectRight> getAllRights(final ApprovalCategory.Id id) {
- Collection<ProjectRight> l = allRights.get(id);
- if (l == null) {
- l = new ArrayList<ProjectRight>();
- l.addAll(getProjectRights(id));
- l.addAll(getWildcardRights(id));
- l = Collections.unmodifiableCollection(l);
- allRights.put(id, l);
- }
- return l;
- }
-
- private static Map<Id, Collection<ProjectRight>> index(
- final Collection<ProjectRight> rights) {
- final HashMap<ApprovalCategory.Id, Collection<ProjectRight>> r;
-
- r = new HashMap<ApprovalCategory.Id, Collection<ProjectRight>>();
- for (final ProjectRight pr : rights) {
- Collection<ProjectRight> l = r.get(pr.getApprovalCategoryId());
- if (l == null) {
- l = new ArrayList<ProjectRight>();
- r.put(pr.getApprovalCategoryId(), l);
- }
- l.add(pr);
- }
- return r;
- }
-
- /**
- * Normalize the approval record down to the range permitted by the type, in
- * case the type was modified since the approval was originally granted.
- * <p>
- * If the record's value was modified, its automatically marked as dirty.
- */
- public void applyTypeFloor(final ApprovalType at, final PatchSetApproval a) {
- final ApprovalCategoryValue atMin = at.getMin();
-
- if (atMin != null && a.getValue() < atMin.getValue()) {
- a.setValue(atMin.getValue());
- dirty(a);
- }
-
- final ApprovalCategoryValue atMax = at.getMax();
- if (atMax != null && a.getValue() > atMax.getValue()) {
- a.setValue(atMax.getValue());
- dirty(a);
- }
- }
-
- /**
- * Normalize the approval record to be inside the maximum range permitted by
- * the ProjectRights granted to groups the account is a member of.
- * <p>
- * If multiple ProjectRights are matched (assigned to different groups the
- * account is a member of) the lowest minValue and the highest maxValue of the
- * union of them is used.
- * <p>
- * If the record's value was modified, its automatically marked as dirty.
- */
- public void applyRightFloor(final PatchSetApproval a) {
- final IdentifiedUser user = userFactory.create(a.getAccountId());
-
- // Find the maximal range actually granted to the user.
- //
- short minAllowed = 0, maxAllowed = 0;
- for (final ProjectRight r : getAllRights(a.getCategoryId())) {
- final AccountGroup.Id grp = r.getAccountGroupId();
- if (user.getEffectiveGroups().contains(grp)) {
- minAllowed = (short) Math.min(minAllowed, r.getMinValue());
- maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
- }
- }
-
- // Normalize the value into that range, returning true if we changed
- // the value.
- //
- if (a.getValue() < minAllowed) {
- a.setValue(minAllowed);
- dirty(a);
-
- } else if (a.getValue() > maxAllowed) {
- a.setValue(maxAllowed);
- dirty(a);
- }
- }
-
- /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */
- public void normalize(final ApprovalType at, final PatchSetApproval ca) {
- applyTypeFloor(at, ca);
- applyRightFloor(ca);
- }
-
- private static Id id(final ApprovalType at) {
- return at.getCategory().getId();
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java b/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java
deleted file mode 100644
index 759d50e66b..0000000000
--- a/src/main/java/com/google/gerrit/server/workflow/MaxWithBlock.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.workflow;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.PatchSetApproval;
-
-/**
- * Computes an {@link ApprovalCategory} by looking at maximum values.
- * <p>
- * In order to be considered "approved" this function requires that:
- * <ul>
- * <li>The maximum negative value is never used;</li>
- * <li>The maximum positive value is used at least once;</li>
- * <li>The user approving the maximum positive has been granted that.</li>
- * </ul>
- * <p>
- * This function is primarily useful for review fields, with values such as:
- * <ul>
- * <li>+2: Approved change.</li>
- * <li>+1: Looks ok, but get another approval from someone with more depth.</li>
- * <li>-1: Soft reject, it isn't a great change but its OK if approved.</li>
- * <li>-2: Rejected, must not be submitted.
- * </ul>
- * <p>
- * Note that projects using this function would typically want to assign out the
- * middle range (-1 .. +1) to almost everyone, so people can indicate how they
- * feel about a change, but the extremes of -2 and +2 should be reserved for the
- * project's long-term maintainers, those who are most familiar with its code.
- */
-public class MaxWithBlock extends CategoryFunction {
- public static String NAME = "MaxWithBlock";
-
- @Override
- public void run(final ApprovalType at, final FunctionState state) {
- boolean rejected = false;
- boolean passed = false;
- for (final PatchSetApproval a : state.getApprovals(at)) {
- state.normalize(at, a);
-
- rejected |= at.isMaxNegative(a);
- passed |= at.isMaxPositive(a);
- }
-
- // The type must not have had its max negative (a forceful reject)
- // and must have at least one max positive (a full accept).
- //
- state.valid(at, !rejected && passed);
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java b/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java
deleted file mode 100644
index 1969128960..0000000000
--- a/src/main/java/com/google/gerrit/server/workflow/NoOpFunction.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.workflow;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.server.CurrentUser;
-
-/** A function that does nothing. */
-public class NoOpFunction extends CategoryFunction {
- public static String NAME = "NoOp";
-
- @Override
- public void run(final ApprovalType at, final FunctionState state) {
- }
-
- @Override
- public boolean isValid(final CurrentUser user, final ApprovalType at,
- final FunctionState state) {
- return false;
- }
-}
diff --git a/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java b/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
deleted file mode 100644
index ac935c5094..0000000000
--- a/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.workflow;
-
-import com.google.gerrit.client.data.ApprovalType;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.Change;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.server.CurrentUser;
-
-/**
- * Computes if the submit function can be used.
- * <p>
- * In order to be considered "approved" this function requires that all approval
- * categories with a position >= 0 (that is any whose
- * {@link ApprovalCategory#isAction()} method returns false) is valid and that
- * the change state be {@link Change.Status#NEW}.
- * <p>
- * This is mostly useful for actions, like {@link ApprovalCategory#SUBMIT}.
- */
-public class SubmitFunction extends CategoryFunction {
- public static String NAME = "Submit";
-
- @Override
- public void run(final ApprovalType at, final FunctionState state) {
- state.valid(at, valid(at, state));
- }
-
- @Override
- public boolean isValid(final CurrentUser user, final ApprovalType at,
- final FunctionState state) {
- if (valid(at, state)) {
- for (final ProjectRight pr : state.getAllRights(at)) {
- if (user.getEffectiveGroups().contains(pr.getAccountGroupId())
- && pr.getMaxValue() > 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static boolean valid(final ApprovalType at, final FunctionState state) {
- if (state.getChange().getStatus() != Change.Status.NEW) {
- return false;
- }
- for (final ApprovalType t : state.getApprovalTypes()) {
- if (!state.isValid(t)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java b/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
deleted file mode 100644
index 30eecd63b6..0000000000
--- a/src/main/java/org/apache/commons/net/smtp/AuthSMTPClient.java
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 org.apache.commons.net.smtp;
-
-import com.google.gerrit.server.ioutil.BlindSSLSocketFactory;
-
-import org.eclipse.jgit.util.Base64;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.SocketException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.crypto.Mac;
-import javax.crypto.spec.SecretKeySpec;
-import javax.net.ssl.SSLSocketFactory;
-
-public class AuthSMTPClient extends SMTPClient {
- private static final Logger log =
- LoggerFactory.getLogger(AuthSMTPClient.class);
-
- private String authTypes;
- private Set<String> allowedRcptTo;
-
- public AuthSMTPClient(final String charset) {
- super(charset);
- }
-
- public void enableSSL(final boolean verify) {
- _socketFactory_ = sslFactory(verify);
- }
-
- public boolean startTLS(final String hostname, final int port,
- final boolean verify) throws SocketException, IOException {
- if (sendCommand("STARTTLS") != 220) {
- return false;
- }
-
- _socket_ = sslFactory(verify).createSocket(_socket_, hostname, port, true);
- _connectAction_();
- return true;
- }
-
- private static SSLSocketFactory sslFactory(final boolean verify) {
- if (verify) {
- return (SSLSocketFactory) SSLSocketFactory.getDefault();
- } else {
- return (SSLSocketFactory) BlindSSLSocketFactory.getDefault();
- }
- }
-
- public void setAllowRcpt(final String[] allowed) {
- if (allowed != null && allowed.length > 0) {
- if (allowedRcptTo == null) {
- allowedRcptTo = new HashSet<String>();
- }
- for (final String addr : allowed) {
- allowedRcptTo.add(addr);
- }
- }
- }
-
- @Override
- public int rcpt(final String forwardPath) throws IOException {
- if (allowRcpt(forwardPath)) {
- return super.rcpt(forwardPath);
- } else {
- log.warn("Not emailing " + forwardPath + " (prohibited by allowrcpt)");
- return SMTPReply.ACTION_OK;
- }
- }
-
- private boolean allowRcpt(String addr) {
- if (allowedRcptTo == null) {
- return true;
- }
- if (addr.startsWith("<") && addr.endsWith(">")) {
- addr = addr.substring(1, addr.length() - 1);
- }
- if (allowedRcptTo.contains(addr)) {
- return true;
- }
- final int at = addr.indexOf('@');
- if (at > 0) {
- return allowedRcptTo.contains(addr.substring(at))
- || allowedRcptTo.contains(addr.substring(at + 1));
- }
- return false;
- }
-
- @Override
- public String[] getReplyStrings() {
- return _replyLines.toArray(new String[_replyLines.size()]);
- }
-
- @Override
- public boolean login() throws IOException {
- final String name = getLocalAddress().getHostName();
- if (name == null) {
- return false;
- }
-
- boolean ok = SMTPReply.isPositiveCompletion(sendCommand("EHLO", name));
- authTypes = "";
- for (String line : getReplyStrings()) {
- if (line != null
- && (line.startsWith("250 AUTH ") || line.startsWith("250-AUTH "))) {
- authTypes = line;
- break;
- }
- }
-
- return ok;
- }
-
- public boolean auth(String smtpUser, String smtpPass) throws IOException {
- List<String> types = Arrays.asList(authTypes.split(" "));
- if ("".equals(authTypes)) {
- // Server didn't advertise authentication support.
- //
- return true;
- }
-
- if (smtpPass == null) {
- smtpPass = "";
- }
- if (types.contains("CRAM-SHA1")) {
- return authCram(smtpUser, smtpPass, "SHA1");
- }
- if (types.contains("CRAM-MD5")) {
- return authCram(smtpUser, smtpPass, "MD5");
- }
- if (types.contains("PLAIN")) {
- return authPlain(smtpUser, smtpPass);
- }
-
- throw new IOException("Unsupported AUTH: " + authTypes);
- }
-
- private boolean authCram(String smtpUser, String smtpPass, String alg)
- throws UnsupportedEncodingException, IOException {
- final String macName = "Hmac" + alg;
- if (sendCommand("AUTH", "CRAM-" + alg) != 334) {
- return false;
- }
-
- final byte[] nonce = Base64.decode(getReplyStrings()[0].split(" ", 2)[1]);
- final String sec;
- try {
- Mac mac = Mac.getInstance(macName);
- mac.init(new SecretKeySpec(smtpPass.getBytes("UTF-8"), macName));
- sec = toHex(mac.doFinal(nonce));
- } catch (NoSuchAlgorithmException e) {
- throw new IOException("Cannot use CRAM-" + alg, e);
- } catch (InvalidKeyException e) {
- throw new IOException("Cannot use CRAM-" + alg, e);
- }
-
- String token = smtpUser + ' ' + sec;
- String cmd = Base64.encodeBytes(token.getBytes("UTF-8"));
- return SMTPReply.isPositiveCompletion(sendCommand(cmd));
- }
-
- private static final char[] hexchar =
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
- 'e', 'f'};
-
- private String toHex(final byte[] b) {
- final StringBuilder sec = new StringBuilder();
- for (int i = 0; i < b.length; i++) {
- final int u = (b[i] >> 4) & 0xf;
- final int l = b[i] & 0xf;
- sec.append(hexchar[u]);
- sec.append(hexchar[l]);
- }
- return sec.toString();
- }
-
- private boolean authPlain(String smtpUser, String smtpPass)
- throws UnsupportedEncodingException, IOException {
- String token = '\0' + smtpUser + '\0' + smtpPass;
- String cmd = "PLAIN " + Base64.encodeBytes(token.getBytes("UTF-8"));
- return SMTPReply.isPositiveCompletion(sendCommand("AUTH", cmd));
- }
-}
diff --git a/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java b/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java
deleted file mode 100644
index 9d001c3cea..0000000000
--- a/src/main/java/org/eclipse/jgit/lib/ObjectIdSerialization.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 org.eclipse.jgit.lib;
-
-import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt32;
-import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-public class ObjectIdSerialization {
- public static void writeCanBeNull(final OutputStream out, final AnyObjectId id)
- throws IOException {
- if (id != null) {
- writeVarInt32(out, 1);
- writeNotNull(out, id);
- } else {
- writeVarInt32(out, 0);
- }
- }
-
- public static void writeNotNull(final OutputStream out, final AnyObjectId id)
- throws IOException {
- writeFixInt32(out, id.w1);
- writeFixInt32(out, id.w2);
- writeFixInt32(out, id.w3);
- writeFixInt32(out, id.w4);
- writeFixInt32(out, id.w5);
- }
-
- public static ObjectId readCanBeNull(final InputStream in) throws IOException {
- switch (readVarInt32(in)) {
- case 0:
- return null;
- case 1:
- return readNotNull(in);
- default:
- throw new IOException("Invalid flag before ObjectId");
- }
- }
-
- public static ObjectId readNotNull(final InputStream in) throws IOException {
- final int w1 = readFixInt32(in);
- final int w2 = readFixInt32(in);
- final int w3 = readFixInt32(in);
- final int w4 = readFixInt32(in);
- final int w5 = readFixInt32(in);
- return new ObjectId(w1, w2, w3, w4, w5);
- }
-
- private ObjectIdSerialization() {
- }
-}
diff --git a/src/main/webapp/WEB-INF/web-jetty.xml b/src/main/webapp/WEB-INF/web-jetty.xml
deleted file mode 100644
index 084d6c68a3..0000000000
--- a/src/main/webapp/WEB-INF/web-jetty.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
-<!--
- This is for hosted mode debugging only.
--->
-<Configure class="org.mortbay.jetty.webapp.WebAppContext">
- <Set name="extraClasspath">target/classes</Set>
-</Configure>
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
deleted file mode 100644
index 4ef561c1ca..0000000000
--- a/src/main/webapp/WEB-INF/web.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app>
- <resource-ref>
- <res-ref-name>jdbc/ReviewDb</res-ref-name>
- <res-type>javax.sql.DataSource</res-type>
- <res-auth>Container</res-auth>
- </resource-ref>
-
- <filter>
- <filter-name>guiceFilter</filter-name>
- <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>guiceFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
-
- <listener>
- <listener-class>com.google.gerrit.server.http.GerritServletConfig</listener-class>
- </listener>
-</web-app>
diff --git a/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java b/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java
deleted file mode 100644
index c1fce7b042..0000000000
--- a/src/test/java/com/google/gerrit/server/config/SystemConfigProviderTest.java
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.config;
-
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.client.reviewdb.ApprovalCategory;
-import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
-import com.google.gerrit.client.reviewdb.Project;
-import com.google.gerrit.client.reviewdb.ProjectRight;
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.client.reviewdb.SchemaVersion;
-import com.google.gerrit.client.reviewdb.SystemConfig;
-import com.google.gerrit.server.workflow.NoOpFunction;
-import com.google.gerrit.server.workflow.SubmitFunction;
-import com.google.gerrit.testutil.TestDatabase;
-import com.google.gwtorm.client.OrmException;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.sql.SQLException;
-import java.util.HashSet;
-
-public class SystemConfigProviderTest extends TestCase {
- private ApprovalCategory.Id codeReview = new ApprovalCategory.Id("CRVW");
- private TestDatabase db;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- db = new TestDatabase();
- }
-
- @Override
- protected void tearDown() throws Exception {
- TestDatabase.drop(db);
- super.tearDown();
- }
-
- public void testGetCauses_CreateSchema() throws OrmException {
- // Initially the schema should be empty.
- //
- try {
- getSchemaVersion();
- fail("Brand new test database has schema_version table");
- } catch (OrmException e) {
- final Throwable cause = e.getCause();
- assertTrue(cause instanceof SQLException);
-
- final String msg = cause.getMessage();
- assertEquals("Table SCHEMA_VERSION not found", msg.split(";")[0]);
- }
-
- // Create the schema using the current schema version.
- //
- final SystemConfig config = getSystemConfig();
- final SchemaVersion version = getSchemaVersion();
- assertNotNull(version);
- assertEquals(ReviewDb.VERSION, version.versionNbr);
-
- assertNotNull(config);
- assertNotNull(config.adminGroupId);
- assertNotNull(config.anonymousGroupId);
- assertNotNull(config.registeredGroupId);
-
- // By default sitePath is set to the current working directory.
- //
- File sitePath = new File(".").getAbsoluteFile();
- if (sitePath.getName().equals(".")) {
- sitePath = sitePath.getParentFile();
- }
- assertEquals(sitePath.getAbsolutePath(), config.sitePath);
-
- // This is randomly generated and should be at least 20 bytes long.
- //
- assertNotNull(config.registerEmailPrivateKey);
- assertTrue(20 < config.registerEmailPrivateKey.length());
- }
-
- public void testSubsequentGetReads() {
- final SystemConfig exp = getSystemConfig();
- final SystemConfig act = getSystemConfig();
-
- assertNotSame(exp, act);
- assertEquals(exp.adminGroupId, act.adminGroupId);
- assertEquals(exp.anonymousGroupId, act.anonymousGroupId);
- assertEquals(exp.registeredGroupId, act.registeredGroupId);
- assertEquals(exp.sitePath, act.sitePath);
- assertEquals(exp.registerEmailPrivateKey, act.registerEmailPrivateKey);
- }
-
- public void testCreateSchema_Group_Administrators() throws OrmException {
- final SystemConfig config = getSystemConfig();
- final ReviewDb c = db.open();
- try {
- final AccountGroup admin = c.accountGroups().get(config.adminGroupId);
- assertNotNull(admin);
- assertEquals(config.adminGroupId, admin.getId());
- assertEquals("Administrators", admin.getName());
- assertSame(AccountGroup.Type.INTERNAL, admin.getType());
- } finally {
- c.close();
- }
- }
-
- public void testCreateSchema_Group_AnonymousUsers() throws OrmException {
- final SystemConfig config = getSystemConfig();
- final ReviewDb c = db.open();
- try {
- final AccountGroup anon = c.accountGroups().get(config.anonymousGroupId);
- assertNotNull(anon);
- assertEquals(config.anonymousGroupId, anon.getId());
- assertEquals("Anonymous Users", anon.getName());
- assertSame(AccountGroup.Type.SYSTEM, anon.getType());
- } finally {
- c.close();
- }
- }
-
- public void testCreateSchema_Group_RegisteredUsers() throws OrmException {
- final SystemConfig config = getSystemConfig();
- final ReviewDb c = db.open();
- try {
- final AccountGroup reg = c.accountGroups().get(config.registeredGroupId);
- assertNotNull(reg);
- assertEquals(config.registeredGroupId, reg.getId());
- assertEquals("Registered Users", reg.getName());
- assertSame(AccountGroup.Type.SYSTEM, reg.getType());
- } finally {
- c.close();
- }
- }
-
- public void testCreateSchema_WildCardProject() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final Project all;
-
- all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
- assertNotNull(all);
- assertEquals("-- All Projects --", all.getName());
- assertEquals(new Project.Id(0), all.getId());
- assertFalse(all.isUseContributorAgreements());
- assertFalse(all.isUseSignedOffBy());
- } finally {
- c.close();
- }
- }
-
- public void testCreateSchema_ApprovalCategory_CodeReview()
- throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(codeReview);
- assertNotNull(cat);
- assertEquals(codeReview, cat.getId());
- assertEquals("Code Review", cat.getName());
- assertEquals("R", cat.getAbbreviatedName());
- assertEquals("MaxWithBlock", cat.getFunctionName());
- assertTrue(cat.isCopyMinScore());
- assertFalse(cat.isAction());
- assertTrue(0 <= cat.getPosition());
- } finally {
- c.close();
- }
- assertValueRange(codeReview, -2, -1, 0, 1, 2);
- }
-
- public void testCreateSchema_ApprovalCategory_Read() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.READ);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.READ, cat.getId());
- assertEquals("Read Access", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.READ, -1, 1, 2);
- }
-
- public void testCreateSchema_ApprovalCategory_Submit() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.SUBMIT);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.SUBMIT, cat.getId());
- assertEquals("Submit", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(SubmitFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.SUBMIT, 1);
- }
-
- public void testCreateSchema_ApprovalCategory_PushTag() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.PUSH_TAG);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.PUSH_TAG, cat.getId());
- assertEquals("Push Annotated Tag", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.PUSH_TAG, //
- ApprovalCategory.PUSH_TAG_SIGNED, //
- ApprovalCategory.PUSH_TAG_ANNOTATED, //
- ApprovalCategory.PUSH_TAG_ANY);
- }
-
- public void testCreateSchema_ApprovalCategory_PushHead() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.PUSH_HEAD);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.PUSH_HEAD, cat.getId());
- assertEquals("Push Branch", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.PUSH_HEAD, //
- ApprovalCategory.PUSH_HEAD_UPDATE, //
- ApprovalCategory.PUSH_HEAD_CREATE, //
- ApprovalCategory.PUSH_HEAD_REPLACE);
- }
-
- public void testCreateSchema_ApprovalCategory_Owner() throws OrmException {
- final ReviewDb c = db.create().open();
- try {
- final ApprovalCategory cat;
-
- cat = c.approvalCategories().get(ApprovalCategory.OWN);
- assertNotNull(cat);
- assertEquals(ApprovalCategory.OWN, cat.getId());
- assertEquals("Owner", cat.getName());
- assertNull(cat.getAbbreviatedName());
- assertEquals(NoOpFunction.NAME, cat.getFunctionName());
- assertTrue(cat.isAction());
- } finally {
- c.close();
- }
- assertValueRange(ApprovalCategory.OWN, 1);
- }
-
- private void assertValueRange(ApprovalCategory.Id cat, int... range)
- throws OrmException {
- final HashSet<ApprovalCategoryValue.Id> act =
- new HashSet<ApprovalCategoryValue.Id>();
- final ReviewDb c = db.open();
- try {
- for (ApprovalCategoryValue v : c.approvalCategoryValues().byCategory(cat)) {
- assertNotNull(v.getId());
- assertNotNull(v.getName());
- assertEquals(cat, v.getCategoryId());
- assertFalse(v.getName().isEmpty());
-
- act.add(v.getId());
- }
- } finally {
- c.close();
- }
-
- for (int value : range) {
- final ApprovalCategoryValue.Id exp =
- new ApprovalCategoryValue.Id(cat, (short) value);
- if (!act.remove(exp)) {
- fail("Category " + cat + " lacks value " + value);
- }
- }
- if (!act.isEmpty()) {
- fail("Category " + cat + " has additional values: " + act);
- }
- }
-
- public void testCreateSchema_DefaultAccess_AnonymousUsers()
- throws OrmException {
- final SystemConfig config = getSystemConfig();
- assertDefaultRight(config.anonymousGroupId, ApprovalCategory.READ, 1, 1);
- }
-
- public void testCreateSchema_DefaultAccess_RegisteredUsers()
- throws OrmException {
- final SystemConfig config = getSystemConfig();
- assertDefaultRight(config.registeredGroupId, ApprovalCategory.READ, 1, 2);
- assertDefaultRight(config.registeredGroupId, codeReview, -1, 1);
- }
-
- public void testCreateSchema_DefaultAccess_Administrators()
- throws OrmException {
- final SystemConfig config = getSystemConfig();
- assertDefaultRight(config.adminGroupId, ApprovalCategory.READ, 1, 1);
- }
-
- private void assertDefaultRight(final AccountGroup.Id group,
- final ApprovalCategory.Id category, int min, int max) throws OrmException {
- final ReviewDb c = db.open();
- try {
- final Project all;
- final ProjectRight right;
-
- all = c.projects().get(WildProjectNameProvider.WILD_PROJECT_ID);
- right = c.projectRights().get( //
- new ProjectRight.Key(all.getNameKey(), category, group));
-
- assertNotNull(right);
- assertEquals(all.getNameKey(), right.getProjectNameKey());
- assertEquals(group, right.getAccountGroupId());
- assertEquals(category, right.getApprovalCategoryId());
- assertEquals(min, right.getMinValue());
- assertEquals(max, right.getMaxValue());
- } finally {
- c.close();
- }
- }
-
- private SystemConfig getSystemConfig() {
- return new SystemConfigProvider(db).get();
- }
-
- private SchemaVersion getSchemaVersion() throws OrmException {
- final ReviewDb c = db.open();
- try {
- return c.schemaVersion().get(new SchemaVersion.Key());
- } finally {
- c.close();
- }
- }
-}
diff --git a/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
deleted file mode 100644
index 81422ab613..0000000000
--- a/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
+++ /dev/null
@@ -1,285 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.mail;
-
-import static org.easymock.EasyMock.createStrictMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-
-import com.google.gerrit.client.reviewdb.Account;
-import com.google.gerrit.client.reviewdb.AccountExternalId;
-import com.google.gerrit.client.reviewdb.AccountGroup;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountState;
-
-import junit.framework.TestCase;
-
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.PersonIdent;
-
-import java.util.Collections;
-
-public class FromAddressGeneratorProviderTest extends TestCase {
- private Config config;
- private PersonIdent ident;
- private AccountCache accountCache;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- config = new Config();
- ident = new PersonIdent("NAME", "e@email", 0, 0);
- accountCache = createStrictMock(AccountCache.class);
- }
-
- private FromAddressGenerator create() {
- return new FromAddressGeneratorProvider(config, ident, accountCache).get();
- }
-
- private void setFrom(final String newFrom) {
- config.setString("sendemail", null, "from", newFrom);
- }
-
- public void testDefaultIsMIXED() {
- assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
- }
-
- public void testSelectUSER() {
- setFrom("USER");
- assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
-
- setFrom("user");
- assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
-
- setFrom("uSeR");
- assertTrue(create() instanceof FromAddressGeneratorProvider.UserGen);
- }
-
- public void testUSER_FullyConfiguredUser() {
- setFrom("USER");
-
- final String name = "A U. Thor";
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(name, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(name, r.name);
- assertEquals(email, r.email);
- verify(accountCache);
- }
-
- public void testUSER_NoFullNameUser() {
- setFrom("USER");
-
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(null, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(null, r.name);
- assertEquals(email, r.email);
- verify(accountCache);
- }
-
- public void testUSER_NoPreferredEmailUser() {
- setFrom("USER");
-
- final Account.Id user = user("A U. Thor", null);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testUSER_NullUser() {
- setFrom("USER");
- replay(accountCache);
- final Address r = create().from(null);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testSelectSERVER() {
- setFrom("SERVER");
- assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
-
- setFrom("server");
- assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
-
- setFrom("sErVeR");
- assertTrue(create() instanceof FromAddressGeneratorProvider.ServerGen);
- }
-
- public void testSERVER_FullyConfiguredUser() {
- setFrom("SERVER");
-
- final String name = "A U. Thor";
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = userNoLookup(name, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testSERVER_NullUser() {
- setFrom("SERVER");
- replay(accountCache);
- final Address r = create().from(null);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testSelectMIXED() {
- setFrom("MIXED");
- assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
-
- setFrom("mixed");
- assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
-
- setFrom("mIxEd");
- assertTrue(create() instanceof FromAddressGeneratorProvider.PatternGen);
- }
-
- public void testMIXED_FullyConfiguredUser() {
- setFrom("MIXED");
-
- final String name = "A U. Thor";
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(name, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(name + " (Code Review)", r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testMIXED_NoFullNameUser() {
- setFrom("MIXED");
-
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(null, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals("Anonymous Coward (Code Review)", r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testMIXED_NoPreferredEmailUser() {
- setFrom("MIXED");
-
- final String name = "A U. Thor";
- final Account.Id user = user(name, null);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals(name + " (Code Review)", r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testMIXED_NullUser() {
- setFrom("MIXED");
- replay(accountCache);
- final Address r = create().from(null);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals(ident.getEmailAddress(), r.email);
- verify(accountCache);
- }
-
- public void testCUSTOM_FullyConfiguredUser() {
- setFrom("A ${user} B <my.server@email.address>");
-
- final String name = "A U. Thor";
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(name, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals("A " + name + " B", r.name);
- assertEquals("my.server@email.address", r.email);
- verify(accountCache);
- }
-
- public void testCUSTOM_NoFullNameUser() {
- setFrom("A ${user} B <my.server@email.address>");
-
- final String email = "a.u.thor@test.example.com";
- final Account.Id user = user(null, email);
-
- replay(accountCache);
- final Address r = create().from(user);
- assertNotNull(r);
- assertEquals("A Anonymous Coward B", r.name);
- assertEquals("my.server@email.address", r.email);
- verify(accountCache);
- }
-
- public void testCUSTOM_NullUser() {
- setFrom("A ${user} B <my.server@email.address>");
-
- replay(accountCache);
- final Address r = create().from(null);
- assertNotNull(r);
- assertEquals(ident.getName(), r.name);
- assertEquals("my.server@email.address", r.email);
- verify(accountCache);
- }
-
- private Account.Id user(final String name, final String email) {
- final AccountState s = makeUser(name, email);
- expect(accountCache.get(eq(s.getAccount().getId()))).andReturn(s);
- return s.getAccount().getId();
- }
-
- private Account.Id userNoLookup(final String name, final String email) {
- final AccountState s = makeUser(name, email);
- return s.getAccount().getId();
- }
-
- private AccountState makeUser(final String name, final String email) {
- final Account.Id userId = new Account.Id(42);
- final Account account = new Account(userId);
- account.setFullName(name);
- account.setPreferredEmail(email);
- final AccountState s =
- new AccountState(account, Collections.<AccountGroup.Id> emptySet(),
- Collections.<AccountExternalId> emptySet());
- return s;
- }
-}
diff --git a/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java b/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
deleted file mode 100644
index 270c5a9494..0000000000
--- a/src/test/java/com/google/gerrit/server/patch/PatchListEntryTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.patch;
-
-import com.google.gerrit.client.reviewdb.Patch;
-
-import junit.framework.TestCase;
-
-public class PatchListEntryTest extends TestCase {
- public void testEmpty1() {
- final String name = "empty-file";
- final PatchListEntry e = PatchListEntry.empty(name);
- assertNull(e.getOldName());
- assertEquals(name, e.getNewName());
- assertSame(Patch.PatchType.UNIFIED, e.getPatchType());
- assertSame(Patch.ChangeType.MODIFIED, e.getChangeType());
- assertTrue(e.getEdits().isEmpty());
- }
-}
diff --git a/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/CommitMsgHookTest.java b/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/CommitMsgHookTest.java
deleted file mode 100644
index 8c7d056ef8..0000000000
--- a/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/CommitMsgHookTest.java
+++ /dev/null
@@ -1,367 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.server.ssh.scproot.hooks;
-
-import org.eclipse.jgit.dircache.DirCache;
-import org.eclipse.jgit.dircache.DirCacheBuilder;
-import org.eclipse.jgit.dircache.DirCacheEntry;
-import org.eclipse.jgit.lib.Commit;
-import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.ObjectWriter;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.RefUpdate;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Date;
-import java.util.TimeZone;
-
-public class CommitMsgHookTest extends HookTestCase {
- private final String SOB1 = "Signed-off-by: J Author <ja@example.com>\n";
- private final String SOB2 = "Signed-off-by: J Committer <jc@example.com>\n";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- final Date when = author.getWhen();
- final TimeZone tz = author.getTimeZone();
-
- author = new PersonIdent("J. Author", "ja@example.com");
- author = new PersonIdent(author, when, tz);
-
- committer = new PersonIdent("J. Committer", "jc@example.com");
- committer = new PersonIdent(committer, when, tz);
- }
-
- public void testEmptyMessages() throws Exception {
- // Empty input must yield empty output so commit will abort.
- // Note we must consider different commit templates formats.
- //
- hookDoesNotModify("");
- hookDoesNotModify(" ");
- hookDoesNotModify("\n");
- hookDoesNotModify("\n\n");
- hookDoesNotModify(" \n ");
-
- hookDoesNotModify("#");
- hookDoesNotModify("#\n");
- hookDoesNotModify("# on branch master\n# Untracked files:\n");
- hookDoesNotModify("\n# on branch master\n# Untracked files:\n");
- hookDoesNotModify("\n\n# on branch master\n# Untracked files:\n");
-
- hookDoesNotModify("\n# on branch master\ndiff --git a/src b/src\n"
- + "new file mode 100644\nindex 0000000..c78b7f0\n");
- }
-
- public void testChangeIdAlreadySet() throws Exception {
- // If a Change-Id is already present in the footer, the hook must
- // not modify the message but instead must leave the identity alone.
- //
- hookDoesNotModify("a\n" + //
- "\n" + //
- "Change-Id: Iaeac9b4149291060228ef0154db2985a31111335\n");
- hookDoesNotModify("fix: this thing\n" + //
- "\n" + //
- "Change-Id: I388bdaf52ed05b55e62a22d0a20d2c1ae0d33e7e\n");
- hookDoesNotModify("fix-a-widget: this thing\n" + //
- "\n" + //
- "Change-Id: Id3bc5359d768a6400450283e12bdfb6cd135ea4b\n");
- hookDoesNotModify("FIX: this thing\n" + //
- "\n" + //
- "Change-Id: I1b55098b5a2cce0b3f3da783dda50d5f79f873fa\n");
- hookDoesNotModify("Fix-A-Widget: this thing\n" + //
- "\n" + //
- "Change-Id: I4f4e2e1e8568ddc1509baecb8c1270a1fb4b6da7\n");
- }
-
- public void testTimeAltersId() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
- call("a\n"));
-
- tick();
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I3251906b99dda598a58a6346d8126237ee1ea800\n",//
- call("a\n"));
-
- tick();
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I69adf9208d828f41a3d7e41afbca63aff37c0c5c\n",//
- call("a\n"));
- }
-
- public void testFirstParentAltersId() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
- call("a\n"));
-
- setHEAD();
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I51e86482bde7f92028541aaf724d3a3f996e7ea2\n",//
- call("a\n"));
- }
-
- public void testDirCacheAltersId() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
- call("a\n"));
-
- final DirCacheBuilder builder = DirCache.lock(repository).builder();
- builder.add(file("A"));
- assertTrue(builder.commit());
-
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: If56597ea9759f23b070677ea6f064c60c38da631\n",//
- call("a\n"));
- }
-
- public void testSingleLineMessages() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
- call("a\n"));
-
- assertEquals("fix: this thing\n" + //
- "\n" + //
- "Change-Id: I0f13d0e6c739ca3ae399a05a93792e80feb97f37\n",//
- call("fix: this thing\n"));
- assertEquals("fix-a-widget: this thing\n" + //
- "\n" + //
- "Change-Id: I1a1a0c751e4273d532e4046a501a612b9b8a775e\n",//
- call("fix-a-widget: this thing\n"));
-
- assertEquals("FIX: this thing\n" + //
- "\n" + //
- "Change-Id: If816d944c57d3893b60cf10c65931fead1290d97\n",//
- call("FIX: this thing\n"));
- assertEquals("Fix-A-Widget: this thing\n" + //
- "\n" + //
- "Change-Id: I3e18d00cbda2ba1f73aeb63ed8c7d57d7fd16c76\n",//
- call("Fix-A-Widget: this thing\n"));
- }
-
- public void testMultiLineMessagesWithoutFooter() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "b\n" + //
- "\n" + //
- "Change-Id: Id0b4f42d3d6fc1569595c9b97cb665e738486f5d\n",//
- call("a\n" + "\n" + "b\n"));
-
- assertEquals("a\n" + //
- "\n" + //
- "b\nc\nd\ne\n" + //
- "\n" + //
- "Change-Id: I7d237b20058a0f46cc3f5fabc4a0476877289d75\n",//
- call("a\n" + "\n" + "b\nc\nd\ne\n"));
-
- assertEquals("a\n" + //
- "\n" + //
- "b\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n",//
- call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n"));
- }
-
- public void testSingleLineMessagesWithSignedOffBy() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
- SOB1,//
- call("a\n" + "\n" + SOB1));
-
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
- SOB1 + //
- SOB2,//
- call("a\n" + "\n" + SOB1 + SOB2));
- }
-
- public void testMultiLineMessagesWithSignedOffBy() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "b\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
- SOB1,//
- call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n" + "\n" + SOB1));
-
- assertEquals("a\n" + //
- "\n" + //
- "b\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- "Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
- SOB1 + //
- SOB2,//
- call("a\n" + //
- "\n" + //
- "b\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- SOB1 + //
- SOB2));
-
- assertEquals("a\n" + //
- "\n" + //
- "b: not a footer\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- "Change-Id: I8869aabd44b3017cd55d2d7e0d546a03e3931ee2\n" + //
- SOB1 + //
- SOB2,//
- call("a\n" + //
- "\n" + //
- "b: not a footer\nc\nd\ne\n" + //
- "\n" + //
- "f\ng\nh\n" + //
- "\n" + //
- SOB1 + //
- SOB2));
- }
-
- public void testNoteInMiddle() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "NOTE: This\n" + //
- "does not fix it.\n" + //
- "\n" + //
- "Change-Id: I988a127969a6ee5e58db546aab74fc46e66847f8\n", //
- call("a\n" + //
- "\n" + //
- "NOTE: This\n" + //
- "does not fix it.\n"));
- }
-
- public void testKernelStyleFooter() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I1bd787f9e7590a2ac82b02c404c955ffb21877c4\n" + //
- SOB1 + //
- "[ja: Fixed\n" + //
- " the indentation]\n" + //
- SOB2, //
- call("a\n" + //
- "\n" + //
- SOB1 + //
- "[ja: Fixed\n" + //
- " the indentation]\n" + //
- SOB2));
- }
-
- public void testChangeIdAfterBugOrIssue() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Bug: 42\n" + //
- "Change-Id: I8c0321227c4324e670b9ae8cf40eccc87af21b1b\n" + //
- SOB1,//
- call("a\n" + //
- "\n" + //
- "Bug: 42\n" + //
- SOB1));
-
- assertEquals("a\n" + //
- "\n" + //
- "Issue: 42\n" + //
- "Change-Id: Ie66e07d89ae5b114c0975b49cf326e90331dd822\n" + //
- SOB1,//
- call("a\n" + //
- "\n" + //
- "Issue: 42\n" + //
- SOB1));
- }
-
- public void testCommitDashV() throws Exception {
- assertEquals("a\n" + //
- "\n" + //
- "Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
- SOB1 + //
- SOB2, //
- call("a\n" + //
- "\n" + //
- SOB1 + //
- SOB2 + //
- "\n" + //
- "# on branch master\n" + //
- "diff --git a/src b/src\n" + //
- "new file mode 100644\n" + //
- "index 0000000..c78b7f0\n"));
- }
-
- private void hookDoesNotModify(final String in) throws Exception {
- assertEquals(in, call(in));
- }
-
- private String call(final String body) throws Exception {
- final File tmp = write(body);
- try {
- final File hook = getHook("commit-msg");
- assertEquals(0, runHook(repository, hook, tmp.getAbsolutePath()));
- return read(tmp);
- } finally {
- tmp.delete();
- }
- }
-
- private DirCacheEntry file(final String name) throws IOException {
- final DirCacheEntry e = new DirCacheEntry(name);
- e.setFileMode(FileMode.REGULAR_FILE);
- e.setObjectId(writer().writeBlob(Constants.encode(name)));
- return e;
- }
-
- private void setHEAD() throws Exception {
- final ObjectWriter ow = writer();
- final Commit commit = new Commit(repository);
- commit.setTreeId(DirCache.newInCore().writeTree(ow));
- commit.setAuthor(author);
- commit.setCommitter(committer);
- commit.setMessage("test\n");
- final ObjectId commitId = ow.writeCommit(commit);
-
- final RefUpdate ref = repository.updateRef(Constants.HEAD);
- ref.setNewObjectId(commitId);
- switch (ref.forceUpdate()) {
- case NEW:
- case FAST_FORWARD:
- case FORCED:
- case NO_CHANGE:
- break;
- default:
- fail(Constants.HEAD + " did not change: " + ref.getResult());
- }
- }
-
- private ObjectWriter writer() {
- return new ObjectWriter(repository);
- }
-}
diff --git a/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/HookTestCase.java b/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/HookTestCase.java
deleted file mode 100644
index a5e45f2235..0000000000
--- a/src/test/java/com/google/gerrit/server/ssh/scproot/hooks/HookTestCase.java
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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.
-//
-// Portions related to finding the hook script to execute are:
-// Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or
-// without modification, are permitted provided that the following
-// conditions are met:
-//
-// - Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//
-// - Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following
-// disclaimer in the documentation and/or other materials provided
-// with the distribution.
-//
-// - Neither the name of the Git Development Community nor the
-// names of its contributors may be used to endorse or promote
-// products derived from this software without specific prior
-// written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package com.google.gerrit.server.ssh.scproot.hooks;
-
-import com.google.gerrit.testutil.LocalDiskRepositoryTestCase;
-
-import org.eclipse.jgit.lib.Repository;
-
-import java.io.File;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-public abstract class HookTestCase extends LocalDiskRepositoryTestCase {
- protected Repository repository;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- repository = createWorkRepository();
- }
-
- protected File getHook(final String name) {
- final String scproot = "com/google/gerrit/server/ssh/scproot";
- final String path = scproot + "/hooks/" + name;
- final URL url = cl().getResource(path);
- if (url == null) {
- fail("Cannot locate " + path + " in CLASSPATH");
- }
-
- File hook;
- try {
- hook = new File(url.toURI());
- } catch (URISyntaxException e) {
- hook = new File(url.getPath());
- }
- if (!hook.isFile()) {
- fail("Cannot locate " + path + " in CLASSPATH");
- }
-
- // The hook was copied out of our source control system into the
- // target area by Java tools. Its not executable in the source
- // are, nor did the copying Java program make it executable in the
- // destination area. So we must force it to be executable.
- //
- final long time = hook.lastModified();
- hook.setExecutable(true);
- hook.setLastModified(time);
- return hook;
- }
-
- private ClassLoader cl() {
- return HookTestCase.class.getClassLoader();
- }
-}
diff --git a/src/test/java/com/google/gerrit/testutil/TestDatabase.java b/src/test/java/com/google/gerrit/testutil/TestDatabase.java
deleted file mode 100644
index d628403c4f..0000000000
--- a/src/test/java/com/google/gerrit/testutil/TestDatabase.java
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (C) 2009 The Android Open Source Project
-//
-// 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 com.google.gerrit.testutil;
-
-import com.google.gerrit.client.reviewdb.ReviewDb;
-import com.google.gerrit.server.config.SystemConfigProvider;
-import com.google.gwtorm.client.OrmException;
-import com.google.gwtorm.client.SchemaFactory;
-import com.google.gwtorm.jdbc.Database;
-import com.google.gwtorm.jdbc.SimpleDataSource;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Properties;
-
-import javax.sql.DataSource;
-
-/**
- * An in-memory test instance of {@link ReviewDb} database.
- * <p>
- * Test classes should create one instance of this class for each unique test
- * database they want to use. When the tests needing this instance are complete,
- * ensure that {@link #drop(TestDatabase)} is called to free the resources so
- * the JVM running the unit tests doesn't run out of heap space.
- */
-public class TestDatabase implements SchemaFactory<ReviewDb> {
- private static int dbCnt;
-
- private static synchronized DataSource newDataSource() throws SQLException {
- final Properties p = new Properties();
- p.setProperty("driver", org.h2.Driver.class.getName());
- p.setProperty("url", "jdbc:h2:mem:" + "Test_" + (++dbCnt));
- final DataSource dataSource = new SimpleDataSource(p);
- return dataSource;
- }
-
- /** Drop the database from memory; does nothing if the instance was null. */
- public static void drop(final TestDatabase db) {
- if (db != null) {
- db.drop();
- }
- }
-
- private Connection openHandle;
- private Database<ReviewDb> database;
-
- public TestDatabase() throws OrmException {
- try {
- final DataSource dataSource = newDataSource();
-
- // Open one connection. This will peg the database into memory
- // until someone calls drop on us, allowing subsequent connections
- // opened against the same URL to go to the same set of tables.
- //
- openHandle = dataSource.getConnection();
-
- // Build the access layer around the connection factory.
- //
- database = new Database<ReviewDb>(dataSource, ReviewDb.class);
- } catch (SQLException e) {
- throw new OrmException(e);
- }
- }
-
- public Database<ReviewDb> getDatabase() {
- return database;
- }
-
- @Override
- public ReviewDb open() throws OrmException {
- return getDatabase().open();
- }
-
- /** Ensure the database schema has been created and initialized. */
- public TestDatabase create() {
- new SystemConfigProvider(this).get();
- return this;
- }
-
- /** Drop this database from memory so it no longer exists. */
- public void drop() {
- if (openHandle != null) {
- try {
- openHandle.close();
- } catch (SQLException e) {
- System.err.println("WARNING: Cannot close database connection");
- e.printStackTrace(System.err);
- }
- openHandle = null;
- database = null;
- }
- }
-}
diff --git a/to_hosted.sh b/to_hosted.sh
deleted file mode 100755
index 247d3d1154..0000000000
--- a/to_hosted.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-
-mvn war:inplace &&
-rm -f src/main/webapp/WEB-INF/lib/gerrit-*.jar
diff --git a/to_jetty.sh b/to_jetty.sh
deleted file mode 100755
index e0f80f4839..0000000000
--- a/to_jetty.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/sh
-
-# Builds and deploys into Jetty; primarily for debugging
-
-jetty=$1
-if [ -z "$jetty" ]
-then
- if [ -z "$JETTY_HOME" ]
- then
- echo >&2 "usage: $0 jettydir"
- exit 1
- fi
- jetty=$JETTY_HOME
-fi
-if ! [ -f "$jetty/etc/jetty.xml" ]
-then
- echo >&2 "error: $jetty is not a Jetty installation"
- exit 1
-fi
-
-ctx="$jetty/contexts/gerrit.xml" &&
-
-mvn clean package &&
-war=target/gerrit-*.war &&
-
-cp $war "$jetty/webapps/gerrit.war" &&
-if [ -f "$ctx" ]
-then
- touch "$ctx"
-else
- rm -f "$jetty/contexts/test.xml" &&
- java -jar $war --cat extra/jetty6/gerrit.xml >"$ctx" &&
-
- echo >&2
- echo >&2 "You need to copy JDBC drivers to $jetty/lib/plus"
- echo >&2 "You need to edit and configure $ctx"
-fi
diff --git a/GoogleFormat.xml b/tools/GoogleFormat.xml
index c0a008a964..c0a008a964 100644
--- a/GoogleFormat.xml
+++ b/tools/GoogleFormat.xml
diff --git a/tools/gwtui_any.launch b/tools/gwtui_any.launch
new file mode 100644
index 0000000000..e565a73a1a
--- /dev/null
+++ b/tools/gwtui_any.launch
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<stringAttribute key="bad_container_name" value="/gerrit-appja"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/gerrit-gwtdbug"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-common&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-httpd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-commonsnet&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-gwtexpui&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-jgit&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-pgm&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-server&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-sshd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-cli&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-ssl&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-reviewdb&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-gwtui&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-gwtdbug&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
+<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.USER_LIBRARY/GWT_17&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;gerrit-gwtui&quot; type=&quot;1&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;gerrit-gwtdbug&quot; type=&quot;1&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-patch-gwtexpui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-patch-jgit/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-reviewdb/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-common/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-gwtui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtexpui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtjsonrpc/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtorm/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl /&#10;-war ${resource_loc:/gerrit-gwtui/target}/gwt-hosted-mode&#10;-server com.google.gerrit.gwtdebug.GerritDebugLauncher&#10;com.google.gerrit.GerritGwtUI"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit-gwtdbug"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M&#10;-DGerritServer=${resource_loc:/gerrit-parent/GerritServer.properties}&#10;-Dcom.google.gerrit.httpd.BecomeAnyAccountLoginServlet=true"/>
+</launchConfiguration>
diff --git a/tools/gwtui_mac.launch b/tools/gwtui_mac.launch
new file mode 100644
index 0000000000..5ce75981fb
--- /dev/null
+++ b/tools/gwtui_mac.launch
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<stringAttribute key="bad_container_name" value="/gerrit-appja"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/gerrit-gwtdbug"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-common&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-httpd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-commonsnet&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-gwtexpui&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-jgit&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-pgm&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-server&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-sshd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-cli&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-ssl&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-reviewdb&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-gwtui&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-gwtdbug&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
+<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.USER_LIBRARY/GWT_17&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;gerrit-gwtui&quot; type=&quot;1&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry path=&quot;3&quot; projectName=&quot;gerrit-gwtdbug&quot; type=&quot;1&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-patch-gwtexpui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-patch-jgit/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-reviewdb/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-common/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gerrit-gwtui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtexpui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtjsonrpc/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwtorm/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl /&#10;-war ${resource_loc:/gerrit-gwtui/target}/gwt-hosted-mode&#10;-server com.google.gerrit.gwtdebug.GerritDebugLauncher&#10;com.google.gerrit.GerritGwtUI"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit-gwtdbug"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XstartOnFirstThread&#10;-Xmx256M&#10;-DGerritServer=${resource_loc:/gerrit-parent/GerritServer.properties}&#10;-Dcom.google.gerrit.httpd.BecomeAnyAccountLoginServlet=true"/>
+</launchConfiguration>
diff --git a/tools/pgm_daemon.launch b/tools/pgm_daemon.launch
new file mode 100644
index 0000000000..30b7f6a639
--- /dev/null
+++ b/tools/pgm_daemon.launch
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<stringAttribute key="bad_container_name" value="/gerrit-appja"/>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/gerrit-main/src/main/java/Main.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-common&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-httpd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-commonsnet&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-patch-jgit&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-pgm&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-server&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-sshd&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-cli&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-util-ssl&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-war&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-reviewdb&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;gerrit-main&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
+<booleanAttribute key="org.eclipse.jdt.debug.ui.CONSIDER_INHERITED_MAIN" value="true"/>
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.classpathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="Main"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="daemon"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="gerrit-war"/>
+<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.maven.ide.eclipse.launchconfig.sourcepathProvider"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M&#10;-DGerritServer=${resource_loc:/gerrit-parent/GerritServer.properties}"/>
+</launchConfiguration>
diff --git a/tools/to_jetty.sh b/tools/to_jetty.sh
new file mode 100755
index 0000000000..28188349d1
--- /dev/null
+++ b/tools/to_jetty.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Builds and deploys into Jetty; primarily for debugging
+
+jetty=$1
+if [ -z "$jetty" ]
+then
+ if [ -z "$JETTY_HOME" ]
+ then
+ echo >&2 "usage: $0 jettydir"
+ exit 1
+ fi
+ jetty=$JETTY_HOME
+fi
+if ! [ -f "$jetty/etc/jetty.xml" ]
+then
+ echo >&2 "error: $jetty is not a Jetty installation"
+ exit 1
+fi
+
+ctx="$jetty/contexts/gerrit.xml" &&
+
+mvn clean package &&
+war=gerrit-war/target/gerrit-*.war &&
+extra=gerrit-war/src/main/webapp/WEB-INF/extra/ &&
+
+cp $war "$jetty/webapps/gerrit.war" &&
+if [ -f "$ctx" ]
+then
+ touch "$ctx"
+else
+ rm -f "$jetty/contexts/test.xml" &&
+ cp "$extra/jetty6/gerrit.xml" "$ctx" &&
+
+ echo >&2
+ echo >&2 "You need to copy JDBC drivers to $jetty/lib/plus"
+ echo >&2 "You need to edit and configure $ctx"
+fi