Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.team.core/.classpath12
-rw-r--r--bundles/org.eclipse.team.core/.cvsignore1
-rw-r--r--bundles/org.eclipse.team.core/.vcm_meta11
-rw-r--r--bundles/org.eclipse.team.core/README13
-rw-r--r--bundles/org.eclipse.team.core/about.html87
-rw-r--r--bundles/org.eclipse.team.core/build.properties3
-rw-r--r--bundles/org.eclipse.team.core/doc/hglegal.htm14
-rw-r--r--bundles/org.eclipse.team.core/doc/ngibmcpy.gifbin0 -> 814 bytes
-rw-r--r--bundles/org.eclipse.team.core/doc/org_eclipse_team_core.html23
-rw-r--r--bundles/org.eclipse.team.core/doc/org_eclipse_team_core_providers.html32
-rw-r--r--bundles/org.eclipse.team.core/plugin.properties1
-rw-r--r--bundles/org.eclipse.team.core/plugin.xml23
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/IFileTypeRegistry.java60
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/IResourceStateChangeListener.java39
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamManager.java132
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamNature.java60
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProvider.java312
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProviderTests.java25
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamException.java65
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java112
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Assert.java100
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java212
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java83
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/TeamManager.java250
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties14
-rw-r--r--bundles/org.eclipse.team.cvs.core/.classpath14
-rw-r--r--bundles/org.eclipse.team.cvs.core/.cvsignore1
-rw-r--r--bundles/org.eclipse.team.cvs.core/.vcm_meta12
-rw-r--r--bundles/org.eclipse.team.cvs.core/about.html87
-rw-r--r--bundles/org.eclipse.team.cvs.core/build.properties3
-rw-r--r--bundles/org.eclipse.team.cvs.core/doc/hglegal.htm14
-rw-r--r--bundles/org.eclipse.team.cvs.core/doc/ngibmcpy.gifbin0 -> 814 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.core/doc/org_eclipse_team_cvs_core.html15
-rw-r--r--bundles/org.eclipse.team.cvs.core/plugin.properties2
-rw-r--r--bundles/org.eclipse.team.cvs.core/plugin.xml44
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFile.java47
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFolder.java36
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteResource.java51
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteRoot.java72
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Attic/CVSStatus.java48
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSDiffException.java25
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSException.java115
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java138
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java1286
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Client.java385
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSRepositoryLocation.java71
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IConnectionMethod.java31
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ILogEntry.java51
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IServerConnection.java1
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserAuthenticator.java64
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserInfo.java34
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java98
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractMessageCommand.java55
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractStructureVisitor.java105
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Add.java123
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AddStructureVisitor.java92
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Admin.java39
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Checkout.java60
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Command.java378
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/CommandDispatcher.java109
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Commit.java61
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Diff.java65
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileNameMatcher.java93
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileStructureVisitor.java103
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ICommand.java56
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Import.java83
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ImportStructureVisitor.java140
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Log.java37
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/PruneFolderVisitor.java39
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Remove.java60
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Status.java38
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Tag.java58
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Update.java90
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSAuthenticationException.java55
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSCommunicationException.java46
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSFileException.java35
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSRepositoryLocation.java570
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSServerException.java21
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/Connection.java339
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnection.java253
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnectionMethod.java1
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/ResourceStatus.java47
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties84
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/RequestSender.java258
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/ValidRequestHandler.java51
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFile.java190
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFolder.java479
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSResource.java297
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FilePropertiesContainer.java275
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFile.java359
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFolder.java603
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedResource.java258
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java122
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFileRevision.java38
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolder.java113
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFile.java148
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFolder.java173
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedResource.java111
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteResource.java89
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteRoot.java85
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ResourceFactory.java185
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSFileNotFoundException.java81
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSProperties.java73
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FileProperties.java287
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FolderProperties.java113
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFile.java93
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFolder.java158
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSResource.java81
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFile.java117
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFolder.java210
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedResource.java121
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedVisitor.java19
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/NotCVSFolderException.java82
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CheckedIn.java96
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CopyHandler.java67
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DefaultHandler.java57
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DumpHandler.java48
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/IResponseHandler.java41
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/MessageOutputHandler.java47
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ModTimeHandler.java92
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/RemoveEntry.java61
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Removed.java62
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseDispatcher.java279
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseHandler.java55
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/StaticHandler.java126
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UnsupportedHandler.java48
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UpdateExisting.java24
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Updated.java132
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/CVSTag.java37
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/ILogListener.java15
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IStatusListener.java23
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IUpdateMessageListener.java29
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogEntry.java84
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogHandler.java177
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogListener.java39
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusErrorHandler.java56
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusMessageHandler.java76
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateErrorHandler.java67
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateMessageHandler.java54
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Assert.java98
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AssertionFailedException.java28
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EmptyTokenizer.java132
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EntriesVisitor.java108
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/FileDateFormat.java41
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ListFileFilter.java84
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionContentHandler.java162
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java169
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionWriter.java101
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java144
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ServerDateFormat.java49
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/StringMatcher.java385
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Util.java342
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/.classpath10
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/.cvsignore1
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/.vcm_meta8
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/about.html48
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/build.properties3
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/plugin.properties1
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/plugin.xml29
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Blowfish.java1214
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Cipher.java19
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Client.java644
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ClientPacket.java47
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Misc.java449
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Packet.java14
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHMethod.java26
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPlugin.java1
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPluginResources.properties20
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHServerConnection.java77
-rw-r--r--bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ServerPacket.java158
-rw-r--r--bundles/org.eclipse.team.cvs.ui/.classpath23
-rw-r--r--bundles/org.eclipse.team.cvs.ui/.cvsignore1
-rw-r--r--bundles/org.eclipse.team.cvs.ui/.vcm_meta14
-rw-r--r--bundles/org.eclipse.team.cvs.ui/about.html87
-rw-r--r--bundles/org.eclipse.team.cvs.ui/build.properties3
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/clcl16/refresh.gifbin0 -> 182 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/ctool16/checkout.gifbin0 -> 121 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/cview16/console_view.gifbin0 -> 166 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/cview16/repo_rep.gifbin0 -> 125 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/obj16/repository_rep.gifbin0 -> 121 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newconnect_wiz.gifbin0 -> 921 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newlocation_wiz.gifbin0 -> 218 bytes
-rw-r--r--bundles/org.eclipse.team.cvs.ui/plugin.properties39
-rw-r--r--bundles/org.eclipse.team.cvs.ui/plugin.xml168
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/AdaptableList.java88
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java92
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSOperationCancelledException.java31
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java195
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java135
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Console.java186
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ConsoleOutputStream.java39
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ICVSUIConstants.java24
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Policy.java83
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java91
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileEditorInput.java191
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileStorage.java41
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RepositoriesView.java186
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ResourcePropertiesPage.java95
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/UserValidationDialog.java186
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/WorkbenchUserAuthenticator.java163
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddAction.java77
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddToWorkspaceAction.java98
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAction.java65
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CommitAction.java90
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/DiffAction.java80
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/OpenRemoteFileAction.java102
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java84
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateAction.java79
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties93
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSAdapterFactory.java41
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSModelElement.java13
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteContentProvider.java32
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFileElement.java42
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFolderElement.java35
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteResourceElement.java25
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteRootElement.java44
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/AddWizard.java47
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizard.java51
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizardPage.java140
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CheckoutWizard.java83
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizard.java84
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizardMainPage.java615
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConnectionWizard.java47
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/LocationWizard.java51
-rw-r--r--bundles/org.eclipse.team.ui/.classpath20
-rw-r--r--bundles/org.eclipse.team.ui/.cvsignore1
-rw-r--r--bundles/org.eclipse.team.ui/.vcm_meta7
-rw-r--r--bundles/org.eclipse.team.ui/README13
-rw-r--r--bundles/org.eclipse.team.ui/about.html87
-rw-r--r--bundles/org.eclipse.team.ui/build.properties3
-rw-r--r--bundles/org.eclipse.team.ui/doc/hglegal.htm14
-rw-r--r--bundles/org.eclipse.team.ui/doc/ngibmcpy.gifbin0 -> 814 bytes
-rw-r--r--bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui.html30
-rw-r--r--bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_configurationWizards.html61
-rw-r--r--bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_decorators.html55
-rw-r--r--bundles/org.eclipse.team.ui/icons/full/ovr/checkedin_ov.gifbin0 -> 876 bytes
-rw-r--r--bundles/org.eclipse.team.ui/icons/full/ovr/checkedout_ov.gifbin0 -> 109 bytes
-rw-r--r--bundles/org.eclipse.team.ui/icons/full/ovr/dirty_ov.gifbin0 -> 109 bytes
-rw-r--r--bundles/org.eclipse.team.ui/plugin.properties26
-rw-r--r--bundles/org.eclipse.team.ui/plugin.xml162
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ASCIIPreferencePage.java1
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardElement.java137
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardNode.java65
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizard.java147
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizardMainPage.java141
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/OverlayIcon.java144
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/Policy.java117
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamResourceDecorator.java336
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/UIConstants.java23
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties57
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/IConfigurationWizard.java30
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ISharedImages.java21
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ITeamDecorator.java46
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/TeamUIPlugin.java179
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckInAction.java69
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckOutAction.java68
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ConfigureProjectAction.java52
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeconfigureProjectAction.java46
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeleteAction.java74
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/MoveAction.java41
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/TeamAction.java298
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ToggleNavigatorDecorations.java62
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UndoCheckOutAction.java69
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UpdateAction.java68
264 files changed, 26347 insertions, 239 deletions
diff --git a/bundles/org.eclipse.team.core/.classpath b/bundles/org.eclipse.team.core/.classpath
new file mode 100644
index 000000000..de56b404b
--- /dev/null
+++ b/bundles/org.eclipse.team.core/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resources.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resourcessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.apache.xerces/xerces.jar" sourcepath="ECLIPSE_HOME/plugins/org.apache.xerces/xercessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtime.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtimesrc.zip"/>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.team.core/.cvsignore b/bundles/org.eclipse.team.core/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/bundles/org.eclipse.team.core/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/.vcm_meta b/bundles/org.eclipse.team.core/.vcm_meta
new file mode 100644
index 000000000..0c9e8d8b7
--- /dev/null
+++ b/bundles/org.eclipse.team.core/.vcm_meta
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <nature id="org.eclipse.pde.PluginNature"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+ <builder name="org.eclipse.pde.ManifestBuilder">
+ </builder>
+ <builder name="org.eclipse.pde.SchemaBuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.team.core/README b/bundles/org.eclipse.team.core/README
new file mode 100644
index 000000000..e5bced73f
--- /dev/null
+++ b/bundles/org.eclipse.team.core/README
@@ -0,0 +1,13 @@
+Note to users of this plug-in
+
+The classes and interfaces in this plug-in are part of an API that is
+still under development and expected to change significantly before
+reaching stability. It is being made available at this early stage to
+solicit feedback from pioneering adopters on the understanding
+that any code that uses this API will almost certainly be broken
+(repeatedly) as the API evolves.
+
+For up-to-date information on these APIs and other related news see
+our component page:
+
+http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-vcm-home/main.html \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/about.html b/bundles/org.eclipse.team.core/about.html
index f3b17d947..9a15e5ca2 100644
--- a/bundles/org.eclipse.team.core/about.html
+++ b/bundles/org.eclipse.team.core/about.html
@@ -1,57 +1,30 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD><TITLE>About</TITLE>
-<META http-equiv=Content-Type content="text/html; charset=windows-1252">
-<STYLE type=text/css>P {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TABLE {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TD {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TH {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-PRE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-H2 {
- FONT-WEIGHT: bold; FONT-SIZE: 18pt; LINE-HEIGHT: 14px; FONT-FAMILY: arial, helvetica, geneva
-}
-CODE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-SUP {
- FONT-SIZE: 10px; FONT-FAMILY: arial,helvetica,geneva
-}
-H3 {
- FONT-WEIGHT: bold; FONT-SIZE: 14pt; FONT-FAMILY: arial, helvetica, geneva
-}
-LI {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-H1 {
- FONT-WEIGHT: bold; FONT-SIZE: 28px; FONT-FAMILY: arial, helvetica, geneva
-}
-BODY {
- MARGIN-TOP: 5mm; FONT-SIZE: 10pt; MARGIN-LEFT: 3mm; FONT-FAMILY: arial, helvetica, geneva
-}
-</STYLE>
-
-<META content="MSHTML 5.50.4522.1800" name=GENERATOR></HEAD>
-<BODY lang=EN-US vLink=purple link=blue>
-<TABLE cellSpacing=5 cellPadding=2 width="100%" border=0>
- <TBODY>
- <TR>
- <TD vAlign=top align=left bgColor=#0080c0 colSpan=2><B><FONT
- face=Arial,Helvetica color=#ffffff>About This Plug-in</FONT></B></TD></TR>
- <TR>
- <TD>
- <P>1st November, 2001</P>
- <H3>License</H3>
- <P>Eclipse.org makes available all content in this plug-in. The plug-in is
- provided to you under the terms and conditions of the <A
- href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License
- Version 0.5</A>. For purposes of the Common Public License, "Program" will
- mean the plug-in.</P></TD></TR></TBODY></TABLE></BODY></HTML>
+<html>
+<head>
+<title>About</title>
+<style type="text/css">
+p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
+pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code { font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup { font-family: arial,helvetica,geneva; font-size: 10px}
+h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li { font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
+</style>
+</head>
+<body>
+<body lang=EN-US link=blue vlink=purple>
+<table border=0 cellspacing=5 cellpadding=2 width="100%" >
+ <tr>
+ <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF" face="Arial,Helvetica">About This Plug-in</font></b></td>
+ </tr>
+ <tr>
+ <td>
+<p>1st November, 2001</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in. The plug-in is provided to you under the terms and conditions of the
+<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a>. For purposes of the Common Public License, &quot;Program&quot; will mean the plug-in.</p>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/build.properties b/bundles/org.eclipse.team.core/build.properties
new file mode 100644
index 000000000..848fe033e
--- /dev/null
+++ b/bundles/org.eclipse.team.core/build.properties
@@ -0,0 +1,3 @@
+# Eclipse build contribution
+bin.includes = about.html,plugin.xml,plugin.properties,*.jar
+source.team.jar=src/
diff --git a/bundles/org.eclipse.team.core/doc/hglegal.htm b/bundles/org.eclipse.team.core/doc/hglegal.htm
new file mode 100644
index 000000000..b071dbdf4
--- /dev/null
+++ b/bundles/org.eclipse.team.core/doc/hglegal.htm
@@ -0,0 +1,14 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Legal Notices</title>
+</head>
+<body>
+
+<h3>
+<a NAME="Notices"></a>Notices</h3>
+(c) Copyright IBM Corp. 2000, 2001. All Rights Reserved.
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.core/doc/ngibmcpy.gif b/bundles/org.eclipse.team.core/doc/ngibmcpy.gif
new file mode 100644
index 000000000..360f8e998
--- /dev/null
+++ b/bundles/org.eclipse.team.core/doc/ngibmcpy.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.core/doc/org_eclipse_team_core.html b/bundles/org.eclipse.team.core/doc/org_eclipse_team_core.html
new file mode 100644
index 000000000..de5e2b364
--- /dev/null
+++ b/bundles/org.eclipse.team.core/doc/org_eclipse_team_core.html
@@ -0,0 +1,23 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+ <title>Team Core Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>Team Core Plug-in</h1></center>
+This document lists all of the extension points that the Team Core plug-in makes available to provider developers.
+<p>
+<hr WIDTH="100%">
+<h1>
+General Extension Points</h1>
+The following extension point can be used to register a team provider nature id with the platform:
+<ul>
+<li>
+<a href="org_eclipse_team_core_providers.html">org.eclipse.team.core.providers</a></li>
+</ul>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001. All Rights Reserved." BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.core/doc/org_eclipse_team_core_providers.html b/bundles/org.eclipse.team.core/doc/org_eclipse_team_core_providers.html
new file mode 100644
index 000000000..2fcc2ff62
--- /dev/null
+++ b/bundles/org.eclipse.team.core/doc/org_eclipse_team_core_providers.html
@@ -0,0 +1,32 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.5 [en] (WinNT; I) [Netscape]">
+ <title>Workbench Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+Applications</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.team.core.providers
+<p><b><i>Description:</i></b> This extension point is used to register
+a provider nature with the Team Core plugin. Registered provider natures
+benefit from the services provided by the Team Core plug-in. These include
+programmatic configuration of providers and cardinality enforcement (e.g.
+only one team nature can be assigned to a project at a time).
+<p>Configuration Markup:
+<p><tt>&lt;!ELEMENT perspective (description?)></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST perspective</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; natureId&nbsp; CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp; ></tt><tt></tt>
+<p><b>natureId</b> - the nature identifier to register
+<p><b><i>Examples:</i></b> The following is an example of a providers extension:
+<p><tt>&lt;extension id="CVSProvider" point="org.eclipse.team.core.providers"></tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; &lt;providers natureId="org.eclipse.team.provider.cvs.core.cvsnature"/></tt>
+<br><tt>&nbsp;&lt;/extension></tt><tt></tt>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001. All Rights Reserved." BORDER=0 height=12 width=195></a>
+</body>
+</html>
+ \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/plugin.properties b/bundles/org.eclipse.team.core/plugin.properties
new file mode 100644
index 000000000..f380eec10
--- /dev/null
+++ b/bundles/org.eclipse.team.core/plugin.properties
@@ -0,0 +1 @@
+pluginName = Eclipse Team Support Core \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/plugin.xml b/bundles/org.eclipse.team.core/plugin.xml
new file mode 100644
index 000000000..ad301bf3b
--- /dev/null
+++ b/bundles/org.eclipse.team.core/plugin.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- File written by PDE 1.0 -->
+<plugin
+ id="org.eclipse.team.core"
+ name="%pluginName"
+ version="2.0.0"
+ provider-name="Object Technology International, Inc."
+ class="org.eclipse.team.core.TeamPlugin">
+<requires>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.core.runtime"/>
+</requires>
+
+<runtime>
+ <library name="team.jar">
+ <export name="*"/>
+ </library>
+</runtime>
+
+<extension-point id="providers" name="Providers"/>
+<extension-point id="fileTypes" name="File Types Registry"/>
+
+</plugin>
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IFileTypeRegistry.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IFileTypeRegistry.java
new file mode 100644
index 000000000..1bf963504
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IFileTypeRegistry.java
@@ -0,0 +1,60 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Provides a generic registry for keys and values based on file extensions.
+ *
+ * File extensions should be considered without the usual "*." prefix.
+ */
+public interface IFileTypeRegistry {
+ /**
+ * Return the value of the given key for the given file extension.
+ * <p>
+ * Example:
+ * <p>
+ * String value = getValue("txt", "isAscii");
+ *
+ * @param extension the extension
+ * @param key the key
+ * @return the value for the given extension and key
+ */
+ public String getValue(String extension, String key);
+ /**
+ * Return all extensions for which the given key is defined.
+ * <p>
+ * Example:
+ * <p>
+ * String[] extensions = getValue("isAscii");
+ *
+ * @param key the key
+ * @return the extensions for which the given key is defined
+ */
+ public String[] getExtensions(String key);
+
+ /**
+ * Set the value of the given key, for files of type extension.
+ * <p>
+ * Example:
+ * <p>
+ * setValue("txt", "isAscii", "true");
+ *
+ * @param extension the file extension
+ * @param key the key
+ * @param value the value
+ */
+ public void setValue(String extension, String key, String value);
+
+ /**
+ * Return whether the registry contains a value for the specified
+ * extension and key.
+ *
+ * @param extension the file extension
+ * @param key the key
+ * @return the value for the extension and key, if applicable
+ */
+ public boolean containsKey(String extension, String key);
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IResourceStateChangeListener.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IResourceStateChangeListener.java
new file mode 100644
index 000000000..73f8a22e8
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/IResourceStateChangeListener.java
@@ -0,0 +1,39 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.EventListener;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * A resource state change listener is notified of changes to resources
+ * regarding their team state.
+ * <p>
+ * Clients may implement this interface.
+ * </p>
+ * @see ITeamManager#addResourceStateChangeListener(IResourceStateChangeListener)
+ */
+public interface IResourceStateChangeListener extends EventListener{
+
+ /**
+ * Notifies this listener that some resource state changes have already
+ * happened. For example, a resource's team state has changed from checked-in
+ * to checked-out.
+ * <p>
+ * Note: This method is called by team core; it is not intended to be called
+ * directly by clients.
+ * </p>
+ *
+ * @param resources that have changed state
+ *
+ * [Note: The changed state event is purposely vague. For now it is only
+ * a hint to listeners that they should query the provider to determine the
+ * resources new team state.]
+ */
+ public void resourceStateChanged(IResource[] changedResources);
+}
+
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamManager.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamManager.java
new file mode 100644
index 000000000..dd6a1a8d3
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamManager.java
@@ -0,0 +1,132 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+
+/**
+ * The <code>ITeamManager</code> is the basis for team provider management
+ * in the platform. There is only one team manager per running platform. All
+ * registered providers exist in the context of this manager. The manager provides
+ * the following services to clients:
+ * <p>
+ * <ul>
+ * <li>find the <code>ITeamProvider</code> associated with
+ * a project.</li>
+ *
+ * <li>associate one provider nature at a time to a project.</li>
+ *
+ * <li>remove a project from being associated with a provider's nature.</li>
+ *
+ * @see ITeamProvider
+ */
+public interface ITeamManager {
+
+ /**
+ * Called to associate a project with a given provider. The Team plug-in will remember
+ * the association between workbench sessions.
+ * <p>
+ * This method allows headless application (e.g. tests, scripts) to create specific providers
+ * programatically. Refer to the provider's documentation for the properties that are required
+ * for initializing a provider programatically.</p>
+ * <p>
+ * Example usage:
+ * <pre>
+ * Properties properties = new Properties();
+ * properties.set("location", "http://www.share.com/~team");
+ * properties.set("httpUser", "team");
+ * properties.set("httpPass", "teampass");
+ * ...
+ * plugin.setProvider(project, "org.eclipse.team.providers.someteam_nature", properties, monitor);
+ * </pre></p>
+ * <p>
+ * Warning: A client using this method is hard coding provider specific details into
+ * their implementations.</p>
+ *
+ * @param project to associate with a specific provider type identified by the natureId.
+ * @param natureId that identifies the provider to associate with the project.
+ * @param configuration required to initialize the provider. The contents of the configuration
+ * is provider specific. Can be <code>null</code> if the provider is configured using another
+ * mechanism.
+ *
+ * @exception TeamException if the project cannot be associated with the provider.
+ * Possible reasons are:
+ * <ul>
+ * <li>provider is already associated with a provider. A client must call
+ * <code>removeProvider</code> before associating a project with
+ * another provider.</li>
+ * <li>provider could not be configured.</li>
+ * </ul></p>
+ */
+ public void setProvider(IProject project, String natureId, Properties configuration, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Answers the provider associated with this resource's project. Returns <code>null</code>
+ * if the project is not associated with a provider.
+ *
+ * @param resource for which to return its associated provider
+ *
+ * @return the team provider instance associated with the resource's project, or <code>null</code>
+ * if the resource's project is not associated with a provider.
+ */
+ public ITeamProvider getProvider(IResource resource);
+
+ /**
+ * Un-associate this project with its provider. If the project is not associated with
+ * a provider this method has no effect.
+ *
+ * @param project to remote the associate to its provider.
+ *
+ * @exception TeamException if the provider cannot be removed from the project. Possible
+ * reasons are:
+ * <ul>
+ * <li>error removing the nature id</li>
+ * </ul>
+ */
+ public void removeProvider(IProject project, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Adds the given listener for provider state change events to this workspace.
+ * Has no effect if an identical listener is already registered for these events.
+ * <p>
+ * Once registered, a listener starts receiving notification of changes to resources states
+ * (e.g. checked in/checked out...) in the workspace the listener continues to receive
+ * notifications until it is replaced or removed.</p>
+ * <p>
+ *
+ * @param listener the listener
+ * @see IResourceStateChangeListener
+ * @see #removeResourceStateChangeListener
+ */
+ public void addResourceStateChangeListener(IResourceStateChangeListener listener);
+
+ /**
+ * Removes the given resource state change listener from this manager.
+ * Has no effect if an identical listener is not registered.
+ *
+ * @param listener the listener
+ * @see IResourceStateChangeListener
+ * @see #addResourceStateChangeListener
+ */
+ public void removeResourceStateChangeListener(IResourceStateChangeListener listener);
+
+ /**
+ * Notify listeners about state changes to the given resources.
+ *
+ * [Note: The changed state event is purposely vague. For now it is only
+ * a hint to listeners that they should query the provider to determine the
+ * resources new team state.]
+ *
+ * @param resources that have changed state.
+ */
+ public void broadcastResourceStateChanges(IResource[] resources);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamNature.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamNature.java
new file mode 100644
index 000000000..a893f01a3
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamNature.java
@@ -0,0 +1,60 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProjectNature;
+
+/**
+ * An interface that provides a team centric nature for providers. Each
+ * provider must have a class that implements this interface and provide
+ * an nature's extension point with this class as the <code>run</code>
+ * parameter.
+ * <p>
+ * A runtime instance of this class will be created for each project that
+ * is associated with a provider. The implementing class must take
+ * advantage of the lifecycle of natures and conform to the operation
+ * specification in <code>IProjectNature</code> interface.</p>
+ * <p>
+ * When a project nature is re-created at runtime (e.g. the workbench
+ * was shutdown and re-started) the <code>setProject()</code>
+ * method will be called and the team nature must re-configure itself
+ * by reading any saved meta information.</p>
+ *
+ * @see IProjectNature
+ * @see ITeamManager
+ * @see ITeamProvider
+ */
+public interface ITeamNature extends IProjectNature {
+
+ /**
+ * Returns a team provider for the given project.
+ * <p>
+ * The returned provider can be used immediately to perform team
+ * operations.
+ * </p>
+ *
+ * @return the <code>ITeamProvider</code> to which this project
+ * nature applies.
+ *
+ * @throws TeamException if the provider cannot be found.
+ */
+ public ITeamProvider getProvider() throws TeamException;
+
+ /**
+ * Configures this project nature given some provider specific configuration
+ * information specified as properties.
+ *
+ * @param configuration the properties used to configure the project.
+ *
+ * @throws TeamException if the provider configuraton fails. Also, if the provider
+ * does not support creating providers programmatically it should throw an exception.
+ *
+ * @see ITeamManager#setProvider(IProject, String, Properties, IProgressMonitor)
+ */
+ public void configureProvider(Properties configuration) throws TeamException;
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProvider.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProvider.java
new file mode 100644
index 000000000..d0c624c59
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProvider.java
@@ -0,0 +1,312 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+ import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+ /**
+ * The <code>ITeamProvider</code> interface exposes a basic team model that
+ * providers should implement to allow third-party plug-ins to perform team operations
+ * programmatically. For example, a code generation tool may want to get source
+ * files before generating the code, and check-in the results. If a team plugin does
+ * not adhere to the <i>semantics</i> of the <code>ITeamProvider</code> interface
+ * as described below, the provider will not be useable programmatically by other
+ * third-party plug-ins.
+ * <p>
+ * All the methods take a <code>IProgressMonitor</code> parameter that allows the
+ * implementation to provide feedback on long running operations (e.g., those involving
+ * large resources, multiple resources or numerous server round-trips). Cancellation is
+ * provided by the progress monitor. When a user cancels an operation the resources
+ * <em>must</em> be left in a consistent state, even when the operations has not
+ * been fully completed. Implementations should throw the
+ * <code>org.eclipse.core.runtime.OperationCanceledException</code> after ensuring
+ * the consistent state of the workspace.</p>
+ * <p>
+ * A number of methods also take a '<code>depth</code>' parameter. In these methods
+ * the <code>depth</code> parameter applies independently to each element of the
+ * <code>resources</code> array. It is used to optimise the case of applying a method to
+ * numerous container resources in the same hierarchy. If the <code>depth</code> value
+ * is <code>IResource.DEPTH_ZERO</code> the method is only applied to the given
+ * container (and not any resources in that container). If the depth value is
+ * <code>IResource.DEPTH_ONE </code> then the method is applied to all non-container
+ * resources in the given container. If the depth parameter value is
+ * <code>IResource.DEPTH_INFINITE</code> then the method is applied to all file
+ * resources in the given container and all sub-containers of the given container.</p>
+ * <p>
+ * <em>Note:</em> The depth parameter values are consistent with their definition in <code>
+ * IResource</code>, however, <i>there are currently no API calls that apply to containers, so
+ * specifying an operation to <code>IResource.DEPTH_ZERO</code> will not have an effect
+ * on any resources.</i>
+ *
+ * @see ITeamNature
+ * @see ITeamManager
+ */
+public interface ITeamProvider {
+
+ /**
+ * Updates the local resource to have the same content as the corresponding remote
+ * resource. Where the local resource does not exist, this method will create it.
+ * <p>
+ * If the remote resource is a container (e.g. folder or project) this operation is equivalent
+ * to getting each non-container member of the remote resource, thereby updating the
+ * content of existing local members, creating local members to receive new remote resources,
+ * and deleting local members that no longer have a corresponding remote resource.</p>
+ * <p>
+ * The method is applied to all resources satisfying the depth parameter, described above.</p>
+ * <p>
+ * Interrupting the method (via the progress monitor) may lead to partial, but consistent, results.</p>
+ *
+ * @param resources an array of local resources to update from the corresponding remote
+ * resources.
+ * @param depth the depth to traverse the given resources, taken from <code>IResource</code>
+ * static constants.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem getting one or more of the resources. The
+ * exception will contain multiple statuses, one for each resource in the <code>resources</code>
+ * array. Possible status codes include:
+ * <ul>
+ * <li>NO_REMOTE_RESOURCE</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ */
+ public void get(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Changes the state of the local resource from checked-in to checked-out and transfers the content
+ * of the remote resource to the local resource.
+ * <p>
+ * Where no corresponding local resource exists in the workspace, one is created (including any
+ * intermediate parent containers) to receive the contents of the remote resource.</p>
+ * <p>
+ * Implementations may optimistically only flag the state change locally and rely on resolving conflicts
+ * during check-in, or they may pessimistically also checkout or lock the remote resource during a
+ * local resource checkout to avoid conflicts. The provider API does not subscribe to either model
+ * and supports each equally.</p>
+ * <p>
+ * Where checkout is applied to a resource that is already checked-out the method has no
+ * effect.</p>
+ *
+ * @param resources the array of local resources to be checked-out.
+ * @param depth the depth to traverse the given resources, taken from <code>IResource</code>
+ * constants.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem checking-out one or more of the resources.
+ * The exception will contain multiple statuses, one for each resource in the <code>resources</code>
+ * array. Possible status codes include:
+ * <ul>
+ * <li>NOT_CHECKED_IN</li>
+ * <li>NO_REMOTE_RESOURCE</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ * @see checkin(IResource[], int, IProgressMonitor)
+ */
+ public void checkout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Transfers the content of the local resource to the corresponding remote resource, and changes the
+ * state of the local resource from checked-out to checked-in.
+ * <p>
+ * If a remote resource does not exist this method creates a new remote resource with the same content
+ * as the given local resource. The local resource is said to <i>correspond</i> to the new remote resource.</p>
+ * <p>
+ * Where providers deal with stores that check-out or lock resources this method is an opportunity
+ * to transfer the content and make the corresponding remote check-in or unlock. It is envisaged that
+ * where the server maintains resource versions, checkin creates a new version of the remote resource.</p>
+ * <p>
+ * Note that some providers may <em>require</em> that a resource is checked-out before it can be
+ * checked-in. However, all providers must support the explicit checking out a resource before checking
+ * it in (e.g., even if the check out is a no-op).</p>
+ *
+ * @param resources an array of local resources to be checked-in.
+ * @param the depth to traverse the given resources, taken from <code>IResource</code>
+ * constants.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem checking-in one or more of the resources.
+ * The exception will contain multiple statuses, one for each resource in the <code>resources</code>
+ * array. Possible status codes include:
+ * <ul>
+ * <li>NOT_CHECKED_OUT</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ * @see checkout(IResource[], int, IProgressMonitor)
+ */
+ public void checkin(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Changes the state of the local resource from checked-out to checked-in without updating the contents
+ * of the remote resource.
+ * <p>
+ * Note that where the provider is a versioning provider, it is envisaged (though not required) that the
+ * uncheckout operation does not create a new version.</p>
+ * <p>
+ * Note also that <code>uncheckout()</code> does not affect the content of the local resource. The
+ * caller is required to perform a <code>get()</code> to revert the local resource if that is required
+ * (otherwise the local resource will be left with the changes that were made while the remote resource
+ * was checked-out. Furthermore, it is valid to call <code>uncheckout()</code> with an
+ * <code>IResource</code> that does not exist locally.</p>
+ *
+ * @param resources an array of the local resources that are to be unchecked-out.
+ * @param depth the depth to traverse the given resources, taken from <code>IResource</code>
+ * constants.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem undoing the check-out of one or more of
+ * the resources. The exception will contain multiple statuses, one for each resource in the
+ * <code>resources</code> array. Possible status codes include:
+ * <ul>
+ * <li>NOT_CHECKED_OUT</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ * @see checkin(IResource)
+ * @see uncheckout(IResource)
+ */
+ public void uncheckout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Deletes the remote resource corresponding to the given local resource.
+ * <p>
+ * The notion of delete is simply to make the remote resource unavailable. Where the provider
+ * supports versioning it is not specified whether the delete operation makes the version
+ * temporarily or forever unavailable, or indeed whether the entire history is made unavailable.</p>
+ * <p>
+ * Note that the <code>IResource</code>'s passed as arguments may be non-existant in the
+ * workbench, the typical case is when such a resource has been received in a core callback.</p>
+ * <p>
+ * The resource may be checked-in or checked-out prior to deletion. The local resource is not
+ * deleted by this method.</p>
+ * <p>
+ * Resource deletions are inherently deep.</p>
+ *
+ * @param resources the array of resources whose corresponding remote resources are to be deleted.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem deleting one or more of
+ * the resources. The exception will contain multiple statuses, one for each resource in the
+ * <code>resources</code> array. Possible status codes include:
+ * <ul>
+ * <li>NO_REMOTE_RESOURCE</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ */
+ public void delete(IResource[] resources, IProgressMonitor progress) throws TeamException;
+
+ /**
+ * Informs the provider that a local resource's name or path has changed.
+ * <p>
+ * Some providers, such as versioning providers, may require this information to track the resource
+ * across name changes.</p>
+ * <p>
+ * Note that this method is always called <em>after</em> the local resource has been moved.</p>
+ *
+ * @param source the full name of the resource before it was moved.
+ * @param target the resource that was moved.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem recording the move. The exception will
+ * contain a single status. Possible status codes are:
+ * <ul>
+ * <li>NO_REMOTE_RESOURCE</li>
+ * <li>IO_FAILED</li>
+ * <li>NOT_AUTHORIZED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ */
+ public void moved(IPath source, IResource target, IProgressMonitor progress) throws TeamException;
+
+ /*
+ * Implementor's Note:
+ * The following methods are required to return promptly (i.e., they may be used to determine the state of
+ * a resource in a UI where long delays are unacceptable). Implementations may cache these values
+ * and update the cache on an explicit call to #refreshState().
+ *
+ * They are currently listed in the provider API, however, they may be moved to a new or different
+ * interface in the future to better reflect their UI-orientation.
+ */
+
+ /**
+ * Answers if the remote resource state is checked-out. If the resource has never been checked in this
+ * method will return <code>true</code>.
+ * <p>
+ * It is undefined whether this method tests for a resource being checked out to this workspace
+ * or any workspace.</p>
+ *
+ * @param resource the local resource to test.
+ * @return <code>true</code> if the resource is checked-out and <code>false</code> if it is not.
+ * @see checkout(IResource[], int, IProgressMonitor)
+ * @see refreshState(IResource[], int, IProgressMonitor)
+ */
+ public boolean isCheckedOut(IResource resource);
+
+ /**
+ * Answers whether the resource has a corresponding remote resource.
+ * <p>
+ * Before a resource is checked-in, the resource will occur locally but not remotely, and calls to this
+ * method will return <code>false</code>. Once a local resource is checked in (and assuming the local
+ * local resource is not moved or the remote resource deleted) there will be a corresponding remote
+ * resource and this method returns <code>true</code>.</p>
+ *
+ * @param resource the local resource to test.
+ * @return <code>true</code> if the local resource has a corresponding remote resource,
+ * and <code>false</code> otherwise.
+ * @see checkin(IResource[], int, IProgressMonitor)
+ * @see refreshState(IResource[], int, IProgressMonitor)
+ */
+ public boolean hasRemote(IResource resource);
+
+ /**
+ * Answer if the local resource currently has a different timestamp to the base timestamp
+ * for this resource.
+ *
+ * @param resource the resource to test.
+ * @return <code>true</code> if the resource has a different modification
+ * timestamp, and <code>false</code> otherwise.
+ */
+ public boolean isDirty(IResource resource);
+
+ /**
+ * Answers true if the base of the given resource is different to the
+ * released state of the given resource.
+ */
+ public boolean isOutOfDate(IResource resource);
+
+ /**
+ * Allows the provider to refresh resource state information for a resource.
+ * <p>
+ * As described above, some state information may be cached by the provider implementation to
+ * avoid server round trips and allow responsive API calls. Where a caller is relying on this
+ * information being current, they should first explicitly refresh the resouce state. Of course, there
+ * are no guarantees that the refreshed information will not become stale immediately after the
+ * call to this method.</p>
+ *
+ * @param resources the array of local resources to be refreshed.
+ * @param depth the depth to traverse the given resources, taken from <code>IResource</code>
+ * constants.
+ * @param progress a progress monitor to indicate the duration of the operation, or
+ * <code>null</code> if progress reporting is not required.
+ * @throws TeamProviderException if there is a problem refreshing one or more of
+ * the resources. The exception will contain multiple statuses, one for each resource in the
+ * <code>resources</code> array. Possible status codes include:
+ * <ul>
+ * <li>IO_FAILED</li>
+ * <li>UNABLE</li>
+ * </ul>
+ */
+ public void refreshState(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException;
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProviderTests.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProviderTests.java
new file mode 100644
index 000000000..658244cb8
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/ITeamProviderTests.java
@@ -0,0 +1,25 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Team plugins can optionally implement this interface to allow running of their providers
+ * against the generic provider test framework.
+ */
+public interface ITeamProviderTests {
+ /**
+ * Allows the test framework to inform the provider to run all further operations
+ * in a unique remote folder. This will provide individual tests with isolated sandboxes.
+ * In addition, using isolated sandboxes for tests allows browsing of the test results
+ * and is valuable for debugging failing tests. Without this support, the test framework
+ * will have to clear the remote location before each test, purging previous test
+ * results.
+ *
+ * @param name of the remote folder in which to perform provider operations.
+ */
+ public void setTestLocation(String name);
+}
+
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamException.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamException.java
new file mode 100644
index 000000000..0354a3878
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamException.java
@@ -0,0 +1,65 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * This exception is thrown by the team provider API. It represents a failure in an API call.
+ * Since some API calls take multiple arguments, the exception is capable of returning multiple
+ * statuses. The API definition determinies if the exception represents a single or multiple status
+ * response; this can also be tested on the exception instance itself.
+ * <p>
+ * To determine the exact cause of the failure the caller should look at each status in detail.</p>
+ */
+public class TeamException extends Exception {
+
+ protected IStatus status = null;
+
+ // Status codes that may appear in a TeamException...
+
+ // The operation completed successfully.
+ public static final int OK = 0;
+
+ // The operation failed because the resource is not checked-in.
+ public static final int NOT_CHECKED_IN = -1;
+
+ // The operation failed because the resource is not checked-out.
+ public static final int NOT_CHECKED_OUT = -2;
+
+ // The corresponding remote resource no longer exists or was never created.
+ public static final int NO_REMOTE_RESOURCE = -3;
+
+ // The provider suffered an IO failure, the operation may be retried.
+ public static final int IO_FAILED = -4;
+
+ // The user is not authorized to execute the attempted operation.
+ public static final int NOT_AUTHORIZED = -5;
+
+ // The provider was unable to complete the operation for an unspecified reason.
+ public static final int UNABLE = -6;
+
+ // The operation cannot be performed due to a conflict with other work.
+ public static final int CONFLICT = -7;
+
+ /**
+ * Single status constructor for a <code>TeamProviderException</code>.
+ */
+ public TeamException(IStatus status) {
+ super(status.getMessage());
+ this.status = status;
+ }
+
+ /**
+ * Answer the single status resulting from the attempted API call.
+ *
+ * @return the single status of the result, or <code>null</code> if this is a multi-status
+ * response.
+ */
+ public IStatus getStatus() {
+ return status;
+ }
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java
new file mode 100644
index 000000000..6c71e44af
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/TeamPlugin.java
@@ -0,0 +1,112 @@
+package org.eclipse.team.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.internal.FileTypeRegistry;
+import org.eclipse.team.core.internal.Policy;
+import org.eclipse.team.core.internal.TeamManager;
+
+/**
+ * <code>TeamPlugin</code> is the plug-in runtime class for the Team
+ * resource management plugin.
+ * <p>
+ * This plugin provides a lightweight registration and lookup service for
+ * associating projects with providers. The registration mechanism is
+ * based on the platform's project natures. Using projects natures allows
+ * manipulating a project in a nature-specific way, for example UI
+ * contributions can be made conditional based on the nature of a project.
+ * </p>
+ *
+ * @see ITeamNature
+ * @see ITeamProvider
+ * @see ITeamManager
+ */
+final public class TeamPlugin extends Plugin {
+
+ // The id of the core team plug-in
+ public static final String ID = "org.eclipse.team.core";
+
+ // The id of the providers extension point
+ public static final String PROVIDER_EXTENSION = "providers";
+
+ // The id of the file types extension point
+ public static final String FILE_TYPES_EXTENSION = "fileTypes";
+
+ // The team manager - manages relationships between projects and providers
+ private static TeamManager manager;
+
+ // The file type registry
+ private static FileTypeRegistry registry;
+
+ // The one and only plug-in instance
+ private static TeamPlugin plugin;
+
+ /**
+ * Constructs a plug-in runtime class for the given plug-in descriptor.
+ */
+ public TeamPlugin(IPluginDescriptor pluginDescriptor) {
+ super(pluginDescriptor);
+ plugin = this;
+ }
+
+ /**
+ * @see Plugin#startup()
+ */
+ public void startup() throws CoreException {
+ try {
+ Policy.localize("org.eclipse.team.core.internal.messages");
+
+ manager = new TeamManager();
+ manager.startup();
+
+ registry = new FileTypeRegistry();
+ registry.startup();
+ } catch(TeamException e) {
+ throw new CoreException(e.getStatus());
+ }
+ }
+
+ /**
+ * @see Plugin#shutdown()
+ */
+ public void shutdown() {
+ registry.shutdown();
+ }
+
+ /**
+ * Returns the Team plug-in.
+ *
+ * @return the single instance of this plug-in runtime class
+ */
+ public static TeamPlugin getPlugin() {
+ return plugin;
+ }
+
+ /**
+ * Returns the team manager.
+ */
+ public static ITeamManager getManager() {
+ return manager;
+ }
+
+ /**
+ * Returns the file type registry.
+ */
+ public static IFileTypeRegistry getFileTypeRegistry() {
+ return registry;
+ }
+
+ /**
+ * Returns the plug-in's log
+ */
+ public static void log(int severity, String message, Throwable e) {
+ plugin.getLog().log(new Status(severity, ID, 0, message, e));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Assert.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Assert.java
new file mode 100644
index 000000000..13ec0baeb
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Assert.java
@@ -0,0 +1,100 @@
+package org.eclipse.team.core.internal;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.core.*;
+
+public final class Assert {
+ public static class AssertionFailedException extends RuntimeException {
+ public AssertionFailedException() {
+ }
+ public AssertionFailedException(String detail) {
+ super(detail);
+ }
+ }
+/* This class is not intended to be instantiated. */
+private Assert() {
+}
+/** Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ *
+ * @param expression the outcode of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+public static boolean isLegal(boolean expression) {
+ return isLegal(expression, ""/*nonNLS*/);
+}
+/** Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcode of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+public static boolean isLegal(boolean expression, String message) {
+ if (!expression)
+ throw new IllegalArgumentException(message);
+ return expression;
+}
+/** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param object the value to test
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+public static void isNotNull(Object object) {
+ isNotNull(object, ""/*nonNLS*/);
+}
+/** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param object the value to test
+ * @param message the message to include in the exception
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+public static void isNotNull(Object object, String message) {
+ if (object == null)
+ throw new AssertionFailedException("null argument:" /*non NLS*/ + message);
+}
+/** Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param expression the outcode of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+public static boolean isTrue(boolean expression) {
+ return isTrue(expression, ""/*nonNLS*/);
+}
+/** Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcode of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+public static boolean isTrue(boolean expression, String message) {
+ if (!expression)
+ throw new AssertionFailedException("assert failed:" /*non NLS*/ + message);
+ return expression;
+}
+/**
+ * Indicates that the caller has not implemented the method.
+ * Usually this is a temporary condition.
+ */
+public static void notYetImplemented() {
+}
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java
new file mode 100644
index 000000000..81aabca94
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/FileTypeRegistry.java
@@ -0,0 +1,212 @@
+package org.eclipse.team.core.internal;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.core.IFileTypeRegistry;
+import org.eclipse.team.core.TeamPlugin;
+
+/**
+ * This class is the temporary home of functionality to determine
+ * whether a particular IResource should be treated as ASCII or Binary.
+ */
+public class FileTypeRegistry implements IFileTypeRegistry {
+ // Constant for the saved state file name
+ private static final String STATE_FILE = ".fileTypeState";
+
+ // The registry hash table
+ private Hashtable registry;
+
+ /**
+ * Create a new FileTypeRegistry.
+ */
+ public FileTypeRegistry() {
+ this.registry = new Hashtable(11);
+ }
+
+ /**
+ * Initialize the registry, restoring its state
+ */
+ public void startup() {
+ loadPluginState();
+ }
+
+ /**
+ * Shut down the registry, persisting its state
+ */
+ public void shutdown() {
+ savePluginState();
+ }
+
+ /**
+ * @see IFileTypeRegistry#getValue(String, String)
+ */
+ public String getValue(String extension, String key) {
+ Hashtable keyTable = (Hashtable)registry.get(extension);
+ if (keyTable == null) return null;
+ return (String)keyTable.get(key);
+ }
+ /**
+ * @see IFileTypeRegistry#getExtensions
+ */
+ public String[] getExtensions(String key) {
+ String[] result = new String[registry.size()];
+ registry.keySet().toArray(result);
+ return result;
+ }
+ /**
+ * @see IFileTypeRegistry#setValue
+ */
+ public void setValue(String extension, String key, String value) {
+ Hashtable keyTable = (Hashtable)registry.get(extension);
+ if (keyTable == null) {
+ keyTable = new Hashtable();
+ registry.put(extension, keyTable);
+ }
+ keyTable.put(key, value);
+ }
+ /**
+ * @see IFileTypeRegistry#containsKey
+ */
+ public boolean containsKey(String extension, String key) {
+ Hashtable keyTable = (Hashtable)registry.get(extension);
+ if (keyTable == null) return false;
+ return ((Hashtable)keyTable).containsKey(key);
+ }
+
+ /**
+ * Reads the ASCII patterns currently defined by extensions.
+ */
+ private void initializePluginPatterns() {
+ TeamPlugin plugin = TeamPlugin.getPlugin();
+ if (plugin != null) {
+ IExtensionPoint extension = plugin.getDescriptor().getExtensionPoint(TeamPlugin.FILE_TYPES_EXTENSION);
+ if (extension != null) {
+ IExtension[] extensions = extension.getExtensions();
+ for (int i = 0; i < extensions.length; i++) {
+ IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
+ for (int j = 0; j < configElements.length; j++) {
+ String ext = configElements[j].getAttribute("extension");
+ if (ext != null) {
+ String key = configElements[j].getAttribute("key");
+ String value = configElements[j].getAttribute("value");
+ // if this pattern doesn't already exist, add it to the registry
+ if (!containsKey(ext, key)) {
+ setValue(ext, key, value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Read the saved file type state from the given input stream.
+ *
+ * @param dis the input stream to read the saved state from
+ * @throws IOException if an I/O problem occurs
+ */
+ private void readState(DataInputStream dis) throws IOException {
+ registry = new Hashtable(11);
+ int extensionCount = 0;
+ try {
+ extensionCount = dis.readInt();
+ } catch (EOFException e) {
+ // Ignore the exception, it will occur if there are no
+ // patterns stored in the state file.
+ return;
+ }
+ for (int i = 0; i < extensionCount; i++) {
+ String extension = dis.readUTF();
+ int keyCount = dis.readInt();
+ for (int j = 0; j < keyCount; j++) {
+ String key = dis.readUTF();
+ String value = dis.readUTF();
+ setValue(extension, key, value);
+ }
+ }
+ }
+ /**
+ * Write the currentstate to the given output stream.
+ *
+ * @param dos the output stream to write the saved state to
+ * @throws IOException if an I/O problem occurs
+ */
+ private void writeState(DataOutputStream dos) throws IOException {
+ dos.writeInt(registry.size());
+ Iterator it = registry.keySet().iterator();
+ while (it.hasNext()) {
+ String extension = (String)it.next();
+ dos.writeUTF(extension);
+ Hashtable keyTable = (Hashtable)registry.get(extension);
+ dos.writeInt(keyTable.size());
+ Iterator keyIt = keyTable.keySet().iterator();
+ while (keyIt.hasNext()) {
+ String key = (String)keyIt.next();
+ dos.writeUTF(key);
+ dos.writeUTF((String)keyTable.get(key));
+ }
+ }
+ }
+ /**
+ * Load the file type registry saved state. This loads the previously saved
+ * contents, as well as discovering any values contributed by plug-ins.
+ */
+ private void loadPluginState() {
+ IPath pluginStateLocation = TeamPlugin.getPlugin().getStateLocation().append(STATE_FILE);
+ File f = pluginStateLocation.toFile();
+ if (f.exists()) {
+ try {
+ DataInputStream dis = new DataInputStream(new FileInputStream(f));
+ readState(dis);
+ dis.close();
+ } catch (IOException ex) {
+ // Throw an exception here
+ }
+ }
+ // Read values contributed by plugins
+ initializePluginPatterns();
+ }
+ /**
+ * Save the file type registry state.
+ */
+ private void savePluginState() {
+ IPath pluginStateLocation = TeamPlugin.getPlugin().getStateLocation();
+ File tempFile = pluginStateLocation.append(STATE_FILE + ".tmp").toFile();
+ File stateFile = pluginStateLocation.append(STATE_FILE).toFile();
+ try {
+ DataOutputStream dos = new DataOutputStream(new FileOutputStream(tempFile));
+ writeState(dos);
+ dos.close();
+ if (stateFile.exists() && !stateFile.delete()) {
+ // Throw an exception here
+ }
+ boolean renamed = tempFile.renameTo(stateFile);
+ if (!renamed) {
+ // Throw an exception here
+ }
+ } catch (Exception e) {
+ // Throw an exception here
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java
new file mode 100644
index 000000000..6081cc096
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/Policy.java
@@ -0,0 +1,83 @@
+package org.eclipse.team.core.internal;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+public class Policy {
+ protected static ResourceBundle bundle = null;
+
+ /**
+ * Creates a NLS catalog for the given locale.
+ */
+ public static void localize(String bundleName) {
+ bundle = ResourceBundle.getBundle(bundleName);
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given string.
+ */
+ public static String bind(String id, String binding) {
+ return bind(id, new String[] { binding });
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given strings.
+ */
+ public static String bind(String id, String binding1, String binding2) {
+ return bind(id, new String[] { binding1, binding2 });
+ }
+
+ /**
+ * Gets a string from the resource bundle. We don't want to crash because of a missing String.
+ * Returns the key if not found.
+ */
+ public static String bind(String key) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and binds it with the given arguments. If the key is
+ * not found, return the key.
+ */
+ public static String bind(String key, Object[] args) {
+ try {
+ return MessageFormat.format(bind(key), args);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Progress monitor helpers
+ */
+ public static void checkCanceled(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ }
+ public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ return monitor;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/TeamManager.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/TeamManager.java
new file mode 100644
index 000000000..543fdb2ed
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/TeamManager.java
@@ -0,0 +1,250 @@
+package org.eclipse.team.core.internal;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.IResourceStateChangeListener;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamNature;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+
+
+public class TeamManager implements ITeamManager {
+
+ private static List natureIdsRegistry = new ArrayList(5);
+ private static List listeners = new ArrayList(5);
+
+ /**
+ * Start the team manager.
+ *
+ * If this method throws an exception, it is taken as an indication that
+ * the manager initialization has failed; as a result, the client should consider
+ * team support disabled.
+ */
+ public void startup() throws TeamException {
+ initializeProviders();
+ }
+
+ protected boolean alreadyMapped(IProject project) {
+ try {
+ String[] natures = project.getDescription().getNatureIds();
+ for (int i = 0; i < natures.length; i++) {
+ if(natureIdsRegistry.contains(natures[i]))
+ return true;
+ }
+ } catch(CoreException e) {
+ // fall through
+ }
+ return false;
+ }
+
+ protected String getFirstProviderNatureId(IProject project) {
+ try {
+ String[] natures = project.getDescription().getNatureIds();
+ for (int i = 0; i < natures.length; i++) {
+ if(natureIdsRegistry.contains(natures[i]))
+ return natures[i];
+ }
+ } catch(CoreException e) {
+ // fall through
+ }
+ return null;
+ }
+
+ /**
+ * @see ITeamManager#setProvider(IProject, String, IProgressMonitor)
+ */
+ public void setProvider(IProject project, String natureId, Properties configuration, IProgressMonitor progress) throws TeamException {
+
+ if(alreadyMapped(project)) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.providerAlreadyMapped",
+ project.getName(), natureId), null));
+ }
+
+ if(!natureIdsRegistry.contains(natureId)) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.notTeamNature",
+ natureId), null));
+ }
+
+ addNatureToProject(project, natureId, progress);
+
+ if(configuration!=null) {
+ setConfiguration(project, natureId, configuration, progress);
+ }
+ }
+
+ protected void setConfiguration(IProject project, String natureId, Properties properties, IProgressMonitor progress) throws TeamException {
+ try {
+ ITeamNature teamNature = (ITeamNature)project.getNature(natureId);
+ teamNature.configureProvider(properties);
+ } catch(ClassCastException e) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.teamNatureBadType",
+ project.getName(), natureId), null));
+ } catch(CoreException e) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.cannotGetDescription",
+ project.getName(), natureId), null));
+
+ }
+ }
+
+ /**
+ * @see ITeamManager#getProvider(IResource resource)
+ */
+ public ITeamProvider getProvider(IResource resource) {
+ IProject project = resource.getProject();
+ String natureId = getFirstProviderNatureId(project);
+
+ if(natureId==null) {
+ return null;
+ }
+
+ try {
+ ITeamNature teamNature = (ITeamNature)project.getNature(natureId);
+ return teamNature.getProvider();
+ } catch(ClassCastException e) {
+ return null;
+ } catch(CoreException e) {
+ return null;
+ } catch(TeamException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Un-associate this project with its provider. If the project is not associated with
+ * a provider this method has no effect.
+ *
+ * @param project to remote the associate to its provider.
+ */
+ public void removeProvider(IProject project, IProgressMonitor progress) throws TeamException {
+ String natureId = getFirstProviderNatureId(project);
+ if(natureId==null) {
+ return;
+ } else {
+ removeNatureFromProject(project, natureId, progress);
+ }
+ }
+
+ /**
+ * Utility for adding a nature to a project
+ */
+ protected void addNatureToProject(IProject proj, String natureId, IProgressMonitor monitor) throws TeamException {
+ try {
+ IProjectDescription description = proj.getDescription();
+ String[] prevNatures= description.getNatureIds();
+ String[] newNatures= new String[prevNatures.length + 1];
+ System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length);
+ newNatures[prevNatures.length]= natureId;
+ description.setNatureIds(newNatures);
+ proj.setDescription(description, monitor);
+ } catch(CoreException e) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorSettingNature",
+ proj.getName(), natureId), e));
+ }
+ }
+ protected void removeNatureFromProject(IProject proj, String natureId, IProgressMonitor monitor) throws TeamException {
+ try {
+ IProjectDescription description = proj.getDescription();
+ String[] prevNatures= description.getNatureIds();
+ List newNatures = new ArrayList(Arrays.asList(prevNatures));
+ newNatures.remove(natureId);
+ description.setNatureIds((String[])newNatures.toArray(new String[newNatures.size()]));
+ proj.setDescription(description, monitor);
+ } catch(CoreException e) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.errorRemovingNature",
+ proj.getName(), natureId), e));
+ }
+ }
+
+ /**
+ * Find and initialize all the registered providers
+ */
+ private void initializeProviders() throws TeamException {
+
+ IExtensionPoint extensionPoint = Platform.getPluginRegistry().getExtensionPoint(TeamPlugin.ID, TeamPlugin.PROVIDER_EXTENSION);
+ if (extensionPoint == null) {
+ throw new TeamException(new Status(IStatus.ERROR, TeamPlugin.ID, 0, Policy.bind("manager.providerExtensionNotFound"), null));
+ }
+
+ IExtension[] extensions = extensionPoint.getExtensions();
+ if (extensions.length == 0)
+ return;
+ for (int i = 0; i < extensions.length; i++) {
+ IExtension extension = extensions[i];
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length == 0) {
+ // there is no configuration element
+ // log as an error but continue to instantiate other executable extensions.
+ TeamPlugin.log(IStatus.ERROR, Policy.bind("manager.providerNoConfigElems", extension.getUniqueIdentifier()), null);
+ continue;
+ }
+ IConfigurationElement config = configs[0];
+ if(config.getName().equalsIgnoreCase(TeamPlugin.PROVIDER_EXTENSION)){
+ String natureId = config.getAttribute("natureId");
+
+ if(natureId!=null) {
+ natureIdsRegistry.add(natureId);
+ } else {
+ // failed to instantiate executable extension.
+ // log the error but continue to instantiate other executable extensions.
+ TeamPlugin.log(IStatus.ERROR, Policy.bind("manager.cannotBadFormat", extension.getUniqueIdentifier()), null);
+ continue;
+ }
+ }
+ }
+ }
+ /*
+ * @see ITeamManager#addResourceStateChangeListener(IResourceStateChangeListener)
+ */
+ public void addResourceStateChangeListener(IResourceStateChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ /*
+ * @see ITeamManager#removeResourceStateChangeListener(IResourceStateChangeListener)
+ */
+ public void removeResourceStateChangeListener(IResourceStateChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /*
+ * @see ITeamManager#broadcastResourceStateChanges(IResource[])
+ */
+ public void broadcastResourceStateChanges(final IResource[] resources) {
+ for(Iterator it=listeners.iterator(); it.hasNext();) {
+ final IResourceStateChangeListener listener = (IResourceStateChangeListener)it.next();
+ ISafeRunnable code = new ISafeRunnable() {
+ public void run() throws Exception {
+ listener.resourceStateChanged(resources);
+ }
+ public void handleException(Throwable e) {
+ // don't log the exception....it is already being logged in Platform#run
+ }
+ };
+ Platform.run(code);
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties
new file mode 100644
index 000000000..5b30c8154
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/internal/messages.properties
@@ -0,0 +1,14 @@
+
+manager.providerAlreadyMapped=Error associating {0} with provider named {1}. The project is already associated with a provider.
+manager.errorFlushSync=Error flushing provider mapping information for {0}.
+manager.errorDeconfigure=Error deconfiguring provider named {0} from project {1}.
+manager.providerTypeInvalid=The provider type is not registered: {0}.
+manager.providerExtensionNotFound=TeamPlugin provider extension not found.
+manager.providerNoConfigElems=No configuration elements found for extension: {0}.
+manager.cannotInstantiateExt=Cannot instantiate extension: {0}.
+manager.errorSerialize=Error serializing provider mappings for {0}.
+manager.errorUnserializeProvider=Cannot unserialize association of {0} with provider of type: {1}. It is no longer a registered provider type.
+manager.errorUnserialize=Error un-serializing provider mappings {0}.
+manager.notTeamNature=Error setting nature: {0} is not a registered team nature.
+manager.errorSettingNature=Error setting nature {1} on project {0}.
+manager.errorRemovingNature=Error removing nature {1} on project {0}. \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/.classpath b/bundles/org.eclipse.team.cvs.core/.classpath
new file mode 100644
index 000000000..127ab892c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/.classpath
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="var" path="ECLIPSE_HOME/plugins/org.apache.xerces/xerces.jar"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.apache.xerces/xerces.jar" sourcepath="ECLIPSE_HOME/plugins/org.apache.xerces/xercessrc.zip"/>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resources.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resourcessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtime.jar" sourcepath="ECLIPSE_ROOT/plugins/org.eclipse.core.runtime/runtimesrc.zip"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="/org.eclipse.team.core"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.team.cvs.core/.cvsignore b/bundles/org.eclipse.team.cvs.core/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/.vcm_meta b/bundles/org.eclipse.team.cvs.core/.vcm_meta
new file mode 100644
index 000000000..7976f78c9
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/.vcm_meta
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <nature id="org.eclipse.pde.PluginNature"/>
+ <reference project-name="org.eclipse.team.core"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+ <builder name="org.eclipse.pde.ManifestBuilder">
+ </builder>
+ <builder name="org.eclipse.pde.SchemaBuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.team.cvs.core/about.html b/bundles/org.eclipse.team.cvs.core/about.html
index f3b17d947..9a15e5ca2 100644
--- a/bundles/org.eclipse.team.cvs.core/about.html
+++ b/bundles/org.eclipse.team.cvs.core/about.html
@@ -1,57 +1,30 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD><TITLE>About</TITLE>
-<META http-equiv=Content-Type content="text/html; charset=windows-1252">
-<STYLE type=text/css>P {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TABLE {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TD {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TH {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-PRE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-H2 {
- FONT-WEIGHT: bold; FONT-SIZE: 18pt; LINE-HEIGHT: 14px; FONT-FAMILY: arial, helvetica, geneva
-}
-CODE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-SUP {
- FONT-SIZE: 10px; FONT-FAMILY: arial,helvetica,geneva
-}
-H3 {
- FONT-WEIGHT: bold; FONT-SIZE: 14pt; FONT-FAMILY: arial, helvetica, geneva
-}
-LI {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-H1 {
- FONT-WEIGHT: bold; FONT-SIZE: 28px; FONT-FAMILY: arial, helvetica, geneva
-}
-BODY {
- MARGIN-TOP: 5mm; FONT-SIZE: 10pt; MARGIN-LEFT: 3mm; FONT-FAMILY: arial, helvetica, geneva
-}
-</STYLE>
-
-<META content="MSHTML 5.50.4522.1800" name=GENERATOR></HEAD>
-<BODY lang=EN-US vLink=purple link=blue>
-<TABLE cellSpacing=5 cellPadding=2 width="100%" border=0>
- <TBODY>
- <TR>
- <TD vAlign=top align=left bgColor=#0080c0 colSpan=2><B><FONT
- face=Arial,Helvetica color=#ffffff>About This Plug-in</FONT></B></TD></TR>
- <TR>
- <TD>
- <P>1st November, 2001</P>
- <H3>License</H3>
- <P>Eclipse.org makes available all content in this plug-in. The plug-in is
- provided to you under the terms and conditions of the <A
- href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License
- Version 0.5</A>. For purposes of the Common Public License, "Program" will
- mean the plug-in.</P></TD></TR></TBODY></TABLE></BODY></HTML>
+<html>
+<head>
+<title>About</title>
+<style type="text/css">
+p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
+pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code { font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup { font-family: arial,helvetica,geneva; font-size: 10px}
+h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li { font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
+</style>
+</head>
+<body>
+<body lang=EN-US link=blue vlink=purple>
+<table border=0 cellspacing=5 cellpadding=2 width="100%" >
+ <tr>
+ <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF" face="Arial,Helvetica">About This Plug-in</font></b></td>
+ </tr>
+ <tr>
+ <td>
+<p>1st November, 2001</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in. The plug-in is provided to you under the terms and conditions of the
+<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a>. For purposes of the Common Public License, &quot;Program&quot; will mean the plug-in.</p>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/build.properties b/bundles/org.eclipse.team.cvs.core/build.properties
new file mode 100644
index 000000000..26098b4f3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/build.properties
@@ -0,0 +1,3 @@
+# Eclipse build contribution
+bin.includes=about.html,plugin.xml,plugin.properties,*.jar
+source.cvs.jar=src/
diff --git a/bundles/org.eclipse.team.cvs.core/doc/hglegal.htm b/bundles/org.eclipse.team.cvs.core/doc/hglegal.htm
new file mode 100644
index 000000000..b071dbdf4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/doc/hglegal.htm
@@ -0,0 +1,14 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Legal Notices</title>
+</head>
+<body>
+
+<h3>
+<a NAME="Notices"></a>Notices</h3>
+(c) Copyright IBM Corp. 2000, 2001. All Rights Reserved.
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.cvs.core/doc/ngibmcpy.gif b/bundles/org.eclipse.team.cvs.core/doc/ngibmcpy.gif
new file mode 100644
index 000000000..360f8e998
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/doc/ngibmcpy.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.core/doc/org_eclipse_team_cvs_core.html b/bundles/org.eclipse.team.cvs.core/doc/org_eclipse_team_cvs_core.html
new file mode 100644
index 000000000..521e209dd
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/doc/org_eclipse_team_cvs_core.html
@@ -0,0 +1,15 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="Author" content="Build">
+ <title>Eclipse CVS Core Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center><h1>Eclipse CVS Core</h1></center>
+The extension points declared by this plug-in are for internal use only.
+<p>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001" BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.cvs.core/plugin.properties b/bundles/org.eclipse.team.cvs.core/plugin.properties
new file mode 100644
index 000000000..5f8281e6f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/plugin.properties
@@ -0,0 +1,2 @@
+pluginName = CVS Team Provider
+cvsNature=CVS Team Nature \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/plugin.xml b/bundles/org.eclipse.team.cvs.core/plugin.xml
new file mode 100644
index 000000000..9a9641cef
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/plugin.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ name="%pluginName"
+ id="org.eclipse.team.cvs.core"
+ version="2.0.0"
+ class="org.eclipse.team.ccvs.core.CVSProviderPlugin"
+ provider-name="Object Technology International, Inc."
+>
+
+ <requires>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.team.core"/>
+ <import plugin="org.apache.xerces"/>
+ </requires>
+
+ <runtime>
+ <library name="cvs.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+ <extension-point name="Authenticator" id="authenticator"/>
+
+ <extension-point name="ConnectionMethods" id="connectionmethods"/>
+
+ <extension id="pserver" point="org.eclipse.team.cvs.core.connectionmethods">
+ <adapter>
+ <run class="org.eclipse.team.internal.ccvs.core.connection.PServerConnectionMethod">
+ <parameter name="trace" value="false" />
+ </run>
+ </adapter>
+ </extension>
+
+ <extension id="CVSProvider" point="org.eclipse.team.core.providers">
+ <providers natureId="org.eclipse.team.cvs.core.cvsnature"/>
+ </extension>
+
+ <extension point="org.eclipse.core.resources.natures" id="cvsnature" name="%cvsNature">
+ <runtime>
+ <run class="org.eclipse.team.ccvs.core.CVSTeamProvider"/>
+ </runtime>
+ </extension>
+
+</plugin> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFile.java
new file mode 100644
index 000000000..9fe809283
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFile.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+
+ /**
+ * This interface represents a file in a repository.
+ * Instances of this interface can be used to fetch the contents
+ * of the remote file.
+ *
+ * In the future, additional information should be available (tags, revisions, etc.)
+ *
+ * Clients are not expected to implement this interface.
+ */
+public interface IRemoteFile extends IRemoteResource {
+
+ /**
+ * Get the contents of the remote file.
+ *
+ * @return an <code>InputStream</code> from which the contents of
+ * the file can be read.
+ *
+ * @throws TeamException if problems occur contacting the server.
+ */
+ public InputStream getContents(IProgressMonitor monitor) throws TeamException;
+
+ /**
+ * Get the log entries of the remote file
+ */
+ public ILogEntry[] getLogEntries(IProgressMonitor monitor) throws TeamException;
+
+ /**
+ * Get the revision of the remote file (e.g. 1.1)
+ *
+ * The revision depends on any tagging associated with the remote parent used
+ * to access the file.
+ */
+ public String getRevision(IProgressMonitor monitor) throws TeamException;
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFolder.java
new file mode 100644
index 000000000..3c59c5acb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteFolder.java
@@ -0,0 +1,36 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+
+ /**
+ * This interface represents a remote folder in a repository. It provides
+ * access to the members (remote files and folders) of a remote folder
+ *
+ * Clients are not expected to implement this interface.
+ */
+public interface IRemoteFolder extends IRemoteResource {
+
+ /**
+ * Get the members of the remote folder.
+ *
+ * <p>
+ * In the case of a simple folder, the <code>getMembers()</code> would return <code>IRemoteResource</code>
+ * instances for each of the files and folders contained in the remote folder.
+ * </p>
+ *
+ * @return an array of <code>IRemoteResource</code> instances which can be cast to
+ * the appropriate sub-interface (<code>IRemoteFolder</code> or <code>IRemoteFile</code>)
+ * based on the type of the resource returned by <code>getType()</code>.
+ *
+ * @throws TeamException if problems occur contacting the server.
+ */
+ public IRemoteResource[] getMembers(IProgressMonitor monitor) throws TeamException;
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteResource.java
new file mode 100644
index 000000000..a09b49209
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteResource.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+
+/**
+ * The interface represents a resource that exists in a CVS repository.
+ * It purpose is to provide information about the remote resource from
+ * the repository.
+ *
+ * Clients are not expected to implement this interface.
+ */
+public interface IRemoteResource extends IAdaptable {
+
+ public static int FILE = IResource.FILE;
+ public static int FOLDER = IResource.FOLDER;
+ public static int ROOT = IResource.PROJECT;
+
+ /**
+ * Return the name of the remote resource.
+ * <p>
+ * For regular files and folders, <code>getName()</code> returns the
+ * unqualified name of the resource. For other remote
+ * resources, such as a repository, name will be more complicated.
+ *
+ * @return the name of the remote resource.
+ */
+ public String getName();
+
+ /**
+ * Return the parent folder of the remote resource.
+ *
+ * @return the parent of the remote resource.
+ */
+ public IRemoteFolder getParent();
+
+ /**
+ * Return the type of the resource.
+ *
+ * @return the type of the remote resource
+ * (either <code>ROOT</code>, <code>FILE</code> or <code>FOLDER</code>)
+ */
+ public int getType();
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteRoot.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteRoot.java
new file mode 100644
index 000000000..8ca62529b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/ccvs/core/IRemoteRoot.java
@@ -0,0 +1,72 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+
+ /**
+ * This interface represents a repository root. As such, it provides
+ * access to the information required to connect to the repository and also
+ * allows the retrieval of the top level modules either from HEAD or for a
+ * particular version or branch tag.
+ *
+ * Clients are not expected to implement this interface.
+ */
+public interface IRemoteRoot extends IRemoteFolder {
+
+ /**
+ * port value which indicates to the default port
+ */
+ public static int DEFAULT_PORT = 0;
+
+ /**
+ * Returns the name of the method used to connect to the
+ * host (e.g. pserver, ext, etc.).
+ *
+ * @return the connection method name.
+ */
+ public String getConnectionMethod();
+
+ /**
+ * Returns the name of the host where the remote root is located.
+ *
+ * @return the host name of the server.
+ */
+ public String getHost();
+
+ /**
+ * Returns the members in the respository that are tagged with the given tag.
+ * The children of any resource returned by this method will also have the associated tag.
+ * Since CVS doesn't tag folders, all folders will be included while
+ * only files with the given tag are included.
+ */
+ public IRemoteResource[] getMembers(final String tagName, final IProgressMonitor monitor) throws TeamException;
+
+ /**
+ * Returns the port used to connect to the host.
+ *
+ * @return the port used to connect to the server or <code>USE_DEFAULT_PORT</code>
+ * if the default port for the connection method is to be used.
+ */
+ public int getPort();
+
+ /**
+ * Return the location of the repository on the server.
+ *
+ * @return the server directory path for the repository location
+
+ */
+ public String getRepositoryPath();
+
+ /**
+ * Returns the username used to connect to the host.
+ *
+ * @return the username for the host connection.
+ */
+ public String getUser();
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Attic/CVSStatus.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Attic/CVSStatus.java
new file mode 100644
index 000000000..623663080
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Attic/CVSStatus.java
@@ -0,0 +1,48 @@
+package org.eclipse.team.internal.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+public class CVSStatus extends Status{
+
+ IPath path;
+
+ public CVSStatus(
+ int type,
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ super(type, CVSProviderPlugin.ID, code, message, exception);
+ this.path = path;
+ }
+
+ public CVSStatus(int code, String message) {
+ this(code, code, null, message, null);
+ }
+
+ public CVSStatus(int code, IPath path, String message) {
+ this(code, code, path, message, null);
+ }
+
+ public CVSStatus(int code, IPath path, String message, Throwable exception) {
+ this(code, code, path, message, exception);
+ }
+
+ /**
+ * @see IResourceStatus#getPath
+ */
+ public IPath getPath() {
+ return path;
+ }
+
+ protected static int getSeverity(int code) {
+ return code;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSDiffException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSDiffException.java
new file mode 100644
index 000000000..d993fa9d8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSDiffException.java
@@ -0,0 +1,25 @@
+package org.eclipse.team.internal.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * This is a special Exception for the diff command and is
+ * thrown when there is a difference between the two
+ * compared files (this is the default behavior of the
+ * diff-command)
+ */
+public class CVSDiffException extends CVSException {
+
+ public CVSDiffException() {
+ super(Policy.bind(("CVSDiffException.message")));
+ }
+
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSException.java
new file mode 100644
index 000000000..461cf8901
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSException.java
@@ -0,0 +1,115 @@
+package org.eclipse.team.internal.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.connection.ResourceStatus;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * This is an exception that is thrown by the cvs-adaptor
+ * for vcm
+ *
+ * @see CoreExcpetion
+ */
+
+public class CVSException extends TeamException {
+
+ public CVSException(
+ int severity,
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ super(new ResourceStatus(severity, code, path, message, exception));
+ }
+ public CVSException(
+ int severity,
+ int code,
+ IPath path,
+ String message) {
+ this(severity, code, path, message, null);
+ }
+ public CVSException(
+ int severity,
+ int code,
+ IPath path,
+ Throwable exception) {
+ this(severity, code, path, null, exception);
+ }
+ public CVSException(
+ int severity,
+ int code,
+ String message,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, message, null));
+ }
+ public CVSException(
+ int severity,
+ int code,
+ String message) {
+ this(severity, code, message, null);
+ }
+
+ public CVSException(
+ int severity,
+ int code,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, null, e));
+
+ }
+
+ public CVSException(String message) {
+ super(new Status(IStatus.ERROR, CVSProviderPlugin.ID, UNABLE, message, null));
+ }
+
+ public CVSException(String message, IPath path) {
+ this(message, path, null);
+ }
+
+ public CVSException(String message, Exception e) {
+ this(IStatus.ERROR, UNABLE, message, e);
+ }
+
+ public CVSException(String message, IPath path, Throwable throwable) {
+ this(new ResourceStatus(IStatus.ERROR, path, message, throwable));
+ }
+ public CVSException(IStatus status) {
+ super(status);
+ }
+
+ /*
+ * Static helper methods for creating exceptions
+ */
+ public static CVSException wrapException(IResource resource, String message,IOException e) {
+ // NOTE: we should record the resource somehow
+ // We should also inlcude the IO message
+ return new CVSException(new Status(
+ IStatus.ERROR,
+ CVSProviderPlugin.ID,
+ IO_FAILED,
+ message,
+ e));
+ }
+ /*
+ * Static helper methods for creating exceptions
+ */
+ public static CVSException wrapException(IResource resource, String message, CoreException e) {
+ return new CVSException(new Status(
+ IStatus.ERROR,
+ CVSProviderPlugin.ID,
+ UNABLE,
+ message,
+ e));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
new file mode 100644
index 000000000..a22774f46
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSProviderPlugin.java
@@ -0,0 +1,138 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.util.ProjectDescriptionManager;
+import org.eclipse.team.internal.ccvs.core.util.ResourceDeltaVisitor;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+public class CVSProviderPlugin extends Plugin {
+
+ private static CVSProviderPlugin instance;
+ /**
+ * Int used for the communications timeout on server connections (in seconds)
+ */
+ public static final int DEFAULT_TIMEOUT = 60;
+ private int communicationsTimeout = DEFAULT_TIMEOUT;
+
+ public static final String ID = "org.eclipse.team.cvs.core";
+ public static final String PT_AUTHENTICATOR = "authenticator";
+ public static final String PT_CONNECTIONMETHODS = "connectionmethods";
+
+ /**
+ * The identifier for the CVS nature
+ * (value <code>"org.eclipse.team.cvs.core.nature"</code>).
+ * The presence of this nature on a project indicates that it is
+ * CVS-capable.
+ *
+ * @see org.eclipse.core.resources.IProject#hasNature
+ */
+ public static final String NATURE_ID = ID + ".cvsnature" ;
+
+ /**
+ * Constructor for CVSProviderPlugin.
+ * @param descriptor
+ */
+ public CVSProviderPlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+ instance = this;
+ }
+
+ /**
+ * Convenience method for logging CVSExceptiuons to the plugin log
+ */
+ public static void log(TeamException e) {
+ // For now, we'll log the status. However we should do more
+ instance.getLog().log(e.getStatus());
+ }
+ public static void log(IStatus status) {
+ // For now, we'll log the status. However we should do more
+ instance.getLog().log(status);
+ }
+
+ /**
+ * Get the communications timeout value in seconds
+ */
+ public int getTimeout() {
+ return communicationsTimeout;
+ }
+ /**
+ * Set the timeout value for communications to a value in seconds.
+ * The value must be greater than or equal 0. If is it 0, there is no timeout.
+ */
+ public void setTimeout(int timeout) {
+ this.communicationsTimeout = Math.max(0, timeout);
+ }
+
+ /**
+ * @see Plugin#startup()
+ */
+ public void startup() throws CoreException {
+ Policy.localize("org.eclipse.team.internal.ccvs.core.messages");
+ ResourceDeltaVisitor.register();
+ }
+
+ /*
+ * Add a resource change listener to the workspace in order to respond to
+ * resource deletions and moves and to ensure or project desription file is up to date.
+ */
+ private void initializeChangeListener() {
+
+ // Build a change listener for changes to thr project meta-information
+ IResourceChangeListener projectChangeListener = new IResourceChangeListener() {
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ IResourceDelta root = event.getDelta();
+ IResourceDelta[] projectDeltas = root.getAffectedChildren(IResourceDelta.CHANGED);
+ for (int i = 0; i < projectDeltas.length; i++) {
+ IResourceDelta delta = projectDeltas[i];
+ IResource resource = delta.getResource();
+ if (resource.getType() == IResource.PROJECT) {
+ IProject project = (IProject)resource;
+ // Get the team provider for the project and
+ ITeamProvider provider = TeamPlugin.getManager().getProvider(project);
+ if (!(provider instanceof CVSTeamProvider)) continue;
+ /* Check if the project description changed. */
+ if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
+ /* The project description changed. Write the file. */
+ ProjectDescriptionManager.writeProjectDescriptionIfNecessary((CVSTeamProvider)provider, project, Policy.monitorFor(null));
+ }
+
+ /* Check if the .vcm_meta file for the project is in the delta. */
+ IResourceDelta[] children = delta.getAffectedChildren(IResourceDelta.REMOVED);
+ for (int j = 0; j < children.length; j++) {
+ IResourceDelta childDelta = children[j];
+ IResource childResource = childDelta.getResource();
+ if (ProjectDescriptionManager.isProjectDescription(childResource)) {
+ ProjectDescriptionManager.writeProjectDescriptionIfNecessary((CVSTeamProvider)provider, project, Policy.monitorFor(null));
+ }
+ }
+ }
+ }
+ } catch (CVSException ex) {
+ Util.logError(Policy.bind("CVSProviderPlugin.cannotUpdateDescription"), ex);
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(projectChangeListener, IResourceChangeEvent.POST_AUTO_BUILD);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
new file mode 100644
index 000000000..15198e7c5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
@@ -0,0 +1,1286 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.IFileTypeRegistry;
+import org.eclipse.team.core.ITeamNature;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.core.CVSDiffException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteResource;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteRoot;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.util.ProjectDescriptionManager;
+
+/**
+ * This class acts as both the ITeamNature and the ITeamProvider instances
+ * required by the Team core.
+ *
+ * The current stat of this class and it's plugin is EXPERIMENTAL.
+ * As such, it is subject to change except in it's conformance to the
+ * TEAM API which it implements.
+ *
+ * Questions:
+ *
+ * How should a project/reource rename/move effect the provider?
+ *
+ * Currently we always update with -P. Is this OK?
+ * - A way to allow customizable options would be nice
+ *
+ * Is the -l option valid for commit and does it work properly for update and commit?
+ *
+ * Do we need an IUserInteractionProvider in the CVS core
+ * - prompt for user info (caching could be separate)
+ * - get release comments
+ * - prompt for overwrite of unmanaged files
+ *
+ * Need a mechanism for communicating meta-information (provided by Team?)
+ *
+ * Should pass null when there are no options for a cvs command
+ *
+ * We currently write the files to disk and do a refreshLocal to
+ * have them appear in Eclipse. This may be changed in the future.
+ */
+public class CVSTeamProvider implements ITeamNature, ITeamProvider {
+
+ // Type related static variables
+ private static final String ID = CVSProviderPlugin.ID;
+ private static final String VERSION = "1.0";
+ public static final QualifiedName TYPE = new QualifiedName(ID, VERSION);
+
+ // Instance variables
+ private IManagedFolder managedProject;
+ private IProject project;
+ private String comment = "";
+
+ private static PrintStream printStream;
+
+ private static String[] DEFAULT_GLOBAL_OPTIONS = new String[] {"-q"};
+
+ private static final CoreException CORE_EXCEPTION = new CoreException(new Status(IStatus.OK, CVSProviderPlugin.ID, TeamException.UNABLE, "", null));
+
+ /**
+ * No-arg Constructor for IProjectNature conformance
+ */
+ public CVSTeamProvider() {
+ }
+
+ /**
+ * @see IProjectNature#configure()
+ */
+ public void configure() throws CoreException {
+ // Do nothing
+ }
+
+ /**
+ * @see IProjectNature#deconfigure()
+ */
+ public void deconfigure() throws CoreException {
+ // unmanage() removes any traces of CVS from the project
+ try {
+ managedProject.unmanage();
+ } catch (CVSException e) {
+ throw new CoreException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, 0, Policy.bind("CVSTeamProvider.deconfigureProblem", new Object[] {project.getName()}), e));
+ }
+ project.refreshLocal(IResource.DEPTH_INFINITE, null);
+ }
+
+ /**
+ * @see IProjectNature#getProject()
+ */
+ public IProject getProject() {
+ return project;
+ }
+
+ /**
+ * @see IProjectNature#setProject(IProject)
+ */
+ public void setProject(IProject project) {
+ this.project = project;
+ try {
+ this.managedProject = Client.getManagedFolder(project.getLocation().toFile());
+ } catch (CVSException e) {
+ // Log any problems creating the CVS managed resource
+ CVSProviderPlugin.log(e);
+ }
+ }
+
+ /**
+ * @see ITeamNature#getProvider()
+ */
+ public ITeamProvider getProvider() throws TeamException {
+ if (managedProject == null) {
+ // An error must have occured when we were configured
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.initializationFailed", new Object[]{project.getName()}), null));
+ }
+ return this;
+ }
+
+ /**
+ * @see ITeamNature#configureProvider(Properties)
+ */
+ public void configureProvider(Properties configuration) throws TeamException {
+ // For now, perform an impiort and checkout.
+ // NOTE: We'll need to revisit this once we start using the Team test framework
+ importAndCheckoutProject(project, configuration, Policy.monitorFor(null));
+ }
+
+ /**
+ * @see ITeamProvider#getType()
+ */
+ public QualifiedName getType() {
+ return TYPE;
+ }
+
+ /*
+ * Build the repository instance from the given properties.
+ * The supported properties are:
+ *
+ * connection The connection method to be used
+ * user The username for the connection
+ * password The password used for the connection (optional)
+ * host The host where the repository resides
+ * port The port to connect to (optional)
+ * root The server directory where the repository is located
+ */
+ private static CVSRepositoryLocation buildRepository(Properties configuration) throws TeamException {
+ StringBuffer repository = new StringBuffer(":");
+ String connection = configuration.getProperty("connection");
+ if (connection == null)
+ repository.append("pserver");
+ else
+ repository.append(connection);
+ repository.append(":");
+ String user = configuration.getProperty("user");
+ if (user == null)
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.noUser"), null));
+ else
+ repository.append(user);
+ repository.append("@");
+ String host = configuration.getProperty("host");
+ if (host == null)
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.noHost"), null));
+ else
+ repository.append(host);
+ String port = configuration.getProperty("port");
+ if (port != null) {
+ repository.append("#");
+ repository.append(port);
+ }
+ repository.append(":");
+ String root = configuration.getProperty("root");
+ if (root == null)
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.noRoot"), null));
+ else
+ repository.append(root);
+
+ CVSRepositoryLocation location = CVSRepositoryLocation.fromString(repository.toString());
+
+ String password = configuration.getProperty("password");
+ if (password != null) {
+ location.setPassword(password);
+ }
+
+ return location;
+ }
+
+ /**
+ * Add the given resources to the project.
+ * <p>
+ * The sematics follow that of CVS in the sense that any folders
+ * being added are created remotely as a result of this operation
+ * while files are created remotely on the next commit.
+ * </p>
+ * <p>
+ * This method uses the team file type registry to determine the type
+ * of added files. If the extension of the file is not in the registry,
+ * the file is assumed to be binary.
+ * </p>
+ * <p>
+ * NOTE: for now we do three operations: one each for folders, text files and binary files.
+ * We should optimize this when time permits to either use one operations or defer server
+ * contact until the next commit.
+ * </p>
+ */
+ public void add(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+
+ // Visit the children of the resources using the depth in order to
+ // determine which folders, text files and binary files need to be added
+ final List folders = new ArrayList(resources.length);
+ final List textfiles = new ArrayList(resources.length);
+ final List binaryfiles = new ArrayList(resources.length);
+ final IFileTypeRegistry registry = TeamPlugin.getFileTypeRegistry();
+ final TeamException[] eHolder = new TeamException[1];
+ for (int i=0;i<resources.length;i++) {
+
+ // Throw an exception if the resource is not a child of the receiver
+ checkIsChild(resources[i]);
+
+ try {
+ // Auto-add parents if they are not already managed
+ IResource parent = resources[i].getParent();
+ List parentFolders = new ArrayList();
+ while (!isManaged(parent)) {
+ parentFolders.add(parent.getFullPath().removeFirstSegments(1).toString());
+ parent = parent.getParent();
+ }
+ for (int j=parentFolders.size()-1;j>=0;j--)
+ folders.add(parentFolders.get(j));
+
+ // Auto-add children
+ resources[i].accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ try {
+ if (!isManaged(resource)) {
+ String name = resource.getFullPath().removeFirstSegments(1).toString();
+ if (resource.getType() == IResource.FILE) {
+ String extension = resource.getFileExtension();
+ if ((extension != null) && ("true".equals(registry.getValue(extension, "isAscii"))))
+ textfiles.add(name);
+ else
+ binaryfiles.add(name);
+ } else
+ folders.add(name);
+ }
+ } catch (TeamException e) {
+ // Record the exception to be thrown again later
+ eHolder[0] = e;
+ return false;
+ }
+ // Always return true and let the depth determine if children are visited
+ return true;
+ }
+ }, depth, false);
+ } catch (CoreException e) {
+ throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.visitError", new Object[] {resources[i].getFullPath()}), e));
+ }
+ }
+ // If an exception occured during the visit, throw it here
+ if (eHolder[0] != null)
+ throw eHolder[0];
+
+ // It looks like we need to add folders first, followed by files!
+ try {
+ if (!folders.isEmpty())
+ Client.execute(
+ Client.ADD,
+ new String[0],
+ new String[0],
+ (String[])folders.toArray(new String[folders.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ if (!textfiles.isEmpty())
+ Client.execute(
+ Client.ADD,
+ new String[0],
+ new String[0],
+ (String[])textfiles.toArray(new String[textfiles.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ if (!binaryfiles.isEmpty()) {
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add(Client.KB_OPTION);
+ // We should check if files are text or not!
+ Client.execute(
+ Client.ADD,
+ new String[0],
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ (String[])binaryfiles.toArray(new String[binaryfiles.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ }
+ } catch (CVSException e) {
+ // Refresh and throw the exception again
+ refreshResources(resources, depth, e, progress);
+ }
+ refreshResources(resources, depth, null, progress);
+ }
+
+ /**
+ * Checkin any local changes using "cvs commit ...".
+ *
+ * @see ITeamProvider#checkin(IResource[], int, IProgressMonitor)
+ */
+ public void checkin(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, depth, progress);
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add(Client.MESSAGE_OPTION);
+ localOptions.add(comment);
+ // If the depth is not infinite, we want the -l option
+ if (depth != IResource.DEPTH_INFINITE)
+ localOptions.add(Client.LOCAL_OPTION);
+
+ // Commit the resources
+ try {
+ Client.execute(
+ Client.COMMIT,
+ DEFAULT_GLOBAL_OPTIONS,
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ arguments,
+ managedProject,
+ progress,
+ getPrintStream());
+ } catch(CVSException e) {
+ refreshResources(resources, depth, e, progress);
+ }
+ refreshResources(resources, depth, null, progress);
+ }
+
+ /**
+ * Checkout the provided resources so they can be modified locally and committed.
+ *
+ * Currently, we support only the optimistic model so checkout dores nothing.
+ *
+ * @see ITeamProvider#checkout(IResource[], int, IProgressMonitor)
+ */
+ public void checkout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+ }
+
+ /*
+ * Generate an exception if the resource is not a child of the project
+ */
+ private void checkIsChild(IResource resource) throws CVSException {
+ if (!isChildResource(resource))
+ throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.invalidResource", new Object[] {resource.getFullPath().toString(), project.getName()}), null));
+ }
+
+ /**
+ * Checkout a CVS module.
+ *
+ * The provided project represents the target project. Any existing contents
+ * may or may not get overwritten. If project is <code>null</code> then a project
+ * will be created based on the provided "module" property. If there is no
+ * "module" property, then the project name will be used as the module to
+ * check out. If both are absent, an exception is thrown.
+ *
+ * After the successful completion of this method, the project will exist
+ * and be open.
+ *
+ * The supported properties are:
+ * connection The connection method to be used
+ * user The username for the connection
+ * password The password used for the connection (optional)
+ * host The host where the repository resides
+ * port The port to connect to (optional)
+ * root The server directory where the repository is located
+ * module The name of the module to be checked out (optional)
+ * tag The tag to be used in the checkout request (optional)
+ */
+ public static void checkout(IProject project, Properties configuration, IProgressMonitor monitor) throws TeamException {
+
+ CVSRepositoryLocation location = buildRepository(configuration);
+ checkout(location, project, configuration.getProperty("module"), configuration.getProperty("tag"), monitor);
+ }
+
+ /**
+ * Checout the provider remote resources into the local workspace.
+ * The local project naming is the same as the above checkout.
+ */
+ public static void checkout(final IRemoteResource[] resources, final IProject[] projects, final IProgressMonitor monitor) throws TeamException {
+
+ final TeamException[] eHolder = new TeamException[1];
+ try {
+ IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor pm) throws CoreException {
+ try {
+ for (int i=0;i<resources.length;i++) {
+ IProject project = null;
+ RemoteResource resource = (RemoteResource)resources[i];
+ if (projects != null)
+ project = projects[i];
+ checkout(resource.getConnection(), project, resource.getFullPath(), null, monitor);
+ }
+ }
+ catch (TeamException e) {
+ // Pass it outside the workspace runnable
+ eHolder[0] = e;
+ }
+ // CoreException and OperationCanceledException are propagated
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(workspaceRunnable, monitor);
+ } catch (CoreException e) {
+ throw wrapException(e);
+ }
+
+ // Re-throw the TeamException, if one occurred
+ if (eHolder[0] != null) {
+ throw eHolder[0];
+ }
+ }
+
+ private static void checkout(CVSRepositoryLocation repository, IProject project, String sourceModule, String tag, IProgressMonitor monitor) throws TeamException {
+ try {
+
+ // Create the project if one wasn't passed.
+ // NOTE: This will need to be fixed for module alias support
+ if (project == null)
+ project = ResourcesPlugin.getWorkspace().getRoot().getProject(new Path(sourceModule).lastSegment());
+
+ // Get the location of the workspace root
+ IManagedFolder root = Client.getManagedFolder(ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile());
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ String module = project.getName();
+ if (sourceModule != null) {
+ localOptions.add(Client.DEEP_OPTION);
+ localOptions.add(module);
+ module = sourceModule;
+ }
+ if (tag != null) {
+ localOptions.add(Client.TAG_OPTION );
+ localOptions.add(tag);
+ }
+
+ // Perform a checkout
+ Client.execute(
+ Client.CHECKOUT,
+ new String[0],
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{module},
+ root,
+ monitor,
+ getPrintStream(),
+ repository,
+ null);
+
+ // Create, open and/or refresh the project
+ if (!project.exists())
+ project.create(monitor);
+ if (!project.isOpen())
+ project.open(monitor);
+ else
+ project.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+
+ // Get the meta file
+ ProjectDescriptionManager.updateProjectIfNecessary(project, monitor);
+
+ // Register the project with Team
+ TeamPlugin.getManager().setProvider(project, CVSProviderPlugin.NATURE_ID, null, monitor);
+
+ // Cache the repository userinfo
+ repository.updateCache();
+
+ snapshot(monitor);
+
+ } catch (CoreException e) {
+ throw wrapException(e);
+ }
+ }
+
+ /**
+ * @see ITeamProvider#delete(IResource[], int, IProgressMonitor)
+ */
+ public void delete(IResource[] resources, final IProgressMonitor progress) throws TeamException {
+
+ // Why does the API state that the file must become unmanaged!
+ // CVS requires the file to be deleted before it can be removed!
+
+ // Concern: I suspect that the file must be deleted but the files parent
+ // must exist for this to work. We may need to modify how Remove works.
+
+ // Could implement a CVSProvider.DELETE!!!
+
+ // Delete any files locally and record the names.
+ // Use a resource visitor to ensure the proper depth is obtained
+ final List files = new ArrayList(resources.length);
+ final Set parents = new HashSet();
+ final TeamException[] eHolder = new TeamException[1];
+ for (int i=0;i<resources.length;i++) {
+ checkIsChild(resources[i]);
+ try {
+ resources[i].accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ try {
+ if (isManaged(resource)) {
+ String name = resource.getFullPath().removeFirstSegments(1).toString();
+ if (resource.getType() == IResource.FILE) {
+ parents.add(resource.getParent());
+ files.add(name);
+ ((IFile)resource).delete(false, true, progress);
+ // NOTE: Should we broadcast Team change events?
+ }
+ }
+ } catch (TeamException e) {
+ eHolder[0] = e;
+ // If there was a problem, don't visit the children
+ return false;
+ } catch (CoreException e) {
+ eHolder[0] = wrapException(e);
+ // If there was a problem, don't visit the children
+ return false;
+ }
+ // Always return true and let the depth determine if children are visited
+ return true;
+ }
+ }, IResource.DEPTH_INFINITE, false);
+ } catch (CoreException e) {
+ throw wrapException(e);
+ }
+ }
+ // If an exception occured during the visit, throw it here
+ if (eHolder[0] != null)
+ throw eHolder[0];
+
+ // Remove the files remotely
+ try {
+ Client.execute(
+ Client.REMOVE,
+ new String[0],
+ new String[0],
+ (String[])files.toArray(new String[files.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ } catch(CVSException e) {
+ refreshResources((IResource[])parents.toArray(new IResource[parents.size()]), IResource.DEPTH_INFINITE, e, progress);
+ }
+ refreshResources((IResource[])parents.toArray(new IResource[parents.size()]), IResource.DEPTH_INFINITE, null, progress);
+ }
+
+ /**
+ * Diff the resources against the repository
+ */
+ public void diff(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, depth, progress);
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ // Perform a context diff
+ localOptions.add("-c");
+ // If the depth is not infinite, we want the -l option
+ if (depth != IResource.DEPTH_INFINITE)
+ localOptions.add(Client.LOCAL_OPTION);
+
+ try {
+ Client.execute(
+ Client.DIFF,
+ new String[] {"-Q"},
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ arguments,
+ managedProject,
+ progress,
+ getPrintStream());
+ } catch(CVSDiffException e) {
+ // Ignore this for now
+ }
+ }
+
+ /**
+ * Temporary method to allow fixing a resources types
+ */
+ public void fixFileType(IResource[] resources,int depth, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list and record any errors.
+ // We need to visit children resources depending on the depth.
+ final TeamException[] eHolder = new TeamException[1]; final List textfiles = new ArrayList(resources.length);
+ final List binaryfiles = new ArrayList(resources.length);
+ final IFileTypeRegistry registry = TeamPlugin.getFileTypeRegistry();
+ for (int i=0;i<resources.length;i++) {
+ checkIsChild(resources[i]);
+ try {
+ resources[i].accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ try {
+ if ((resource.getType() == IResource.FILE) && (isManaged(resource))) {
+ String name = resource.getFullPath().removeFirstSegments(1).toString();
+ String extension = resource.getFileExtension();
+ if ((extension != null) && ("true".equals(registry.getValue(extension, "isAscii"))))
+ textfiles.add(name);
+ else
+ binaryfiles.add(name);
+ }
+ } catch (TeamException e) {
+ eHolder[0] = e;
+ // If there was a problem, don't visit the children
+ return false;
+ }
+ // Always return true and let the depth determine if children are visited
+ return true;
+ }
+ }, depth, false);
+ } catch (CoreException e) {
+ throw wrapException(e);
+ }
+ }
+ // If an exception occured during the visit, throw it here
+ if (eHolder[0] != null)
+ throw eHolder[0];
+
+ if (!textfiles.isEmpty()) {
+ List localOptions = new ArrayList();
+ localOptions.add(Client.KO_OPTION); // disable keyword substitution
+ Client.execute(
+ Client.ADMIN,
+ new String[0],
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ (String[])textfiles.toArray(new String[textfiles.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ }
+ if (!binaryfiles.isEmpty()) {
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add(Client.KB_OPTION); // disable keyword substitution
+ Client.execute(
+ Client.ADMIN,
+ new String[0],
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ (String[])binaryfiles.toArray(new String[binaryfiles.size()]),
+ managedProject,
+ progress,
+ getPrintStream());
+ }
+
+ // Update the options on the local files
+ List localOptions = new ArrayList();
+ if (depth != IResource.DEPTH_INFINITE)
+ // If depth = zero or 1, use -l
+ localOptions.add(Client.LOCAL_OPTION);
+ localOptions.add("-A");
+ update(resources, depth, (String[])localOptions.toArray(new String[localOptions.size()]), progress);
+ }
+
+ /**
+ * Replace the local version of the provided resources with the remote using "cvs update -C ..."
+ *
+ * @see ITeamProvider#get(IResource[], int, IProgressMonitor)
+ */
+ public void get(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, depth, progress);
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add("-C"); // Ignore any local changes
+ if (depth == IResource.DEPTH_INFINITE)
+ // if depth = infinite, look for new directories
+ localOptions.add(Client.DEEP_OPTION);
+ else
+ // If depth = zero or 1, use -l
+ localOptions.add(Client.LOCAL_OPTION);
+
+ try {
+ Client.execute(
+ Client.UPDATE,
+ new String[0],
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ arguments,
+ managedProject,
+ progress,
+ getPrintStream());
+ } catch(CVSException e) {
+ refreshResources(resources, depth, e, progress);
+ }
+ refreshResources(resources, depth, null, progress);
+ }
+
+ /*
+ * Returns all patterns in the given project that should be treated as binary
+ */
+ private static String[] getBinaryFilePatterns(IProject project) throws TeamException {
+ final IFileTypeRegistry registry = TeamPlugin.getFileTypeRegistry();
+ final Set result = new HashSet();
+ try {
+ project.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ if (resource.getType() == IResource.FILE) {
+ String extension = resource.getFileExtension();
+ if (extension == null) {
+ result.add(resource.getName());
+ } else if (!("true".equals(registry.getValue(extension, "isAscii")))) {
+ result.add("*." + extension);
+ }
+ }
+ // Always return true and let the depth determine if children are visited
+ return true;
+ }
+ }, IResource.DEPTH_INFINITE, false);
+ } catch (CoreException e) {
+ throw wrapException(e);
+ }
+ return (String[])result.toArray(new String[result.size()]);
+ }
+
+ /*
+ * Get the corresponding managed child for the given resource.
+ */
+ private IManagedResource getChild(IResource resource) throws CVSException {
+ if (resource.equals(project))
+ return managedProject;
+ return managedProject.getChild(resource.getFullPath().removeFirstSegments(1).toString());
+ }
+
+ /**
+ * Answer the name of the connection method for the given resource's
+ * project.
+ */
+ public String getConnectionMethod(IResource resource) throws TeamException {
+ checkIsChild(resource);
+ return CVSRepositoryLocation.fromString(managedProject.getFolderInfo().getRoot()).getMethod().getName();
+ }
+
+ /**
+ * Get the names of the registered connection methods.
+ */
+ public static String[] getConnectionMethods() {
+ IConnectionMethod[] methods = CVSRepositoryLocation.getPluggedInConnectionMethods();
+ String[] result = new String[methods.length];
+ for (int i=0;i<methods.length;i++)
+ result[i] = methods[i].getName();
+ return result;
+ }
+
+ /**
+ * Get the print stream to which information from CVS commands
+ * is sent.
+ */
+ public static PrintStream getPrintStream() {
+ if (printStream == null)
+ return System.out;
+ else
+ return printStream;
+ }
+
+ /**
+ * Get the remote root (i.e. CVS repository) associated with
+ * the provided parameters.
+ *
+ * The supported properties are:
+ * connection The connection method to be used
+ * user The username for the connection
+ * password The password used for the connection (optional)
+ * host The host where the repository resides
+ * port The port to connect to (optional)
+ * root The server directory where the repository is located
+ */
+ public static IRemoteRoot getRemoteRoot(Properties configuration) throws TeamException {
+ return new RemoteRoot(buildRepository(configuration));
+ }
+
+ /**
+ * Returns an IUserInfo instance that can be used to access and set the
+ * user name and set the password. To have changes take place, the user must
+ * invoke the setUserInfo() method.
+ */
+ public IUserInfo getUserInfo(IResource resource) throws TeamException {
+ checkIsChild(resource);
+ CVSRepositoryLocation location = CVSRepositoryLocation.fromString(managedProject.getFolderInfo().getRoot());
+ location.setUserMuteable(true);
+ return location;
+ }
+
+ /*
+ * Get the arguments to be passed to a commit or update
+ */
+ private String[] getValidArguments(IResource[] resources, int depth, IProgressMonitor progress) throws CVSException {
+ List arguments = new ArrayList(resources.length);
+ for (int i=0;i<resources.length;i++) {
+ checkIsChild(resources[i]);
+ // A depth of zero is only valid for files
+ if ((depth != IResource.DEPTH_ZERO) || (resources[i].getType() == IResource.FILE)) {
+ IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1);
+ if (cvsPath.segmentCount() == 0)
+ arguments.add(".");
+ else
+ arguments.add(cvsPath.toString());
+ }
+ }
+ return (String[])arguments.toArray(new String[arguments.size()]);
+ }
+
+ /**
+ * @see ITeamProvider#hasRemote(IResource)
+ */
+ public boolean hasRemote(IResource resource) {
+ try {
+ // This assumes that all projects associated with a provider exists remotely
+ // which is the case presently
+ if (resource.equals(project))
+ return isManaged(resource);
+
+ IManagedResource child = getChild(resource);
+ if (!child.isManaged())
+ return false;
+ if (resource.getType() == IResource.FOLDER) {
+ // if it's managed and its a folder than it exists remotely
+ return true;
+ } else {
+ // NOTE: This seems rather adhoc!
+ return !((IManagedFile)child).getFileInfo().getVersion().equals("0");
+ }
+ } catch (TeamException e) {
+ // Shouldn't have got an exception. Since we did, log it and return false
+ CVSProviderPlugin.log(e);
+ return false;
+ }
+ }
+
+ /**
+ * @see ITeamProvider#isLocallyCheckedOut(IResource)
+ */
+ public boolean isCheckedOut(IResource resource) {
+ // check to see if the resource exists and has an entry
+ try {
+ return isManaged(resource);
+ } catch (TeamException e) {
+ // Something went wrong. Log it and say the file is not checked out
+ CVSProviderPlugin.log(e);
+ return false;
+ }
+ }
+
+ /*
+ * Helper to indicate if the resource is a child of the receiver's project
+ */
+ private boolean isChildResource(IResource resource) {
+ return resource.getProject().getName().equals(managedProject.getName());
+ }
+
+ /**
+ * @see ITeamSynch#isDirty(IResource)
+ */
+ public boolean isDirty(IResource resource) {
+ if (!isChildResource(resource))
+ return false;
+ try {
+ resource.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ try {
+ IManagedResource r = getChild(resource);
+ boolean ignored = r.isIgnored();
+
+ if (ignored)
+ return false;
+
+ // for projects continue checking children
+ if(resource.getType()==IResource.PROJECT) {
+ return true;
+ }
+
+ // mark additions as dirty to remind that they should be commited
+ if(!r.isManaged() && !ignored) {
+ throw CORE_EXCEPTION;
+ }
+
+ // for files that are managed calculate their dirty state
+ if( !r.isFolder() ) {
+ IManagedFile file = (IManagedFile)r;
+ if( file.isDirty() ) {
+ throw CORE_EXCEPTION;
+ }
+ }
+
+ // continue looking at children
+ return true;
+ } catch(CVSException e) {
+ return false;
+ }
+ }
+ }, IResource.DEPTH_INFINITE, false);
+ } catch (CoreException e) {
+ //if our exception was caught, we know there's a dirty child
+ return e == CORE_EXCEPTION;
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the given resource is managed.
+ *
+ * From a CVS standpoint, this means that we have a CVS entry
+ * for the resource and that uodates and commits may effect the
+ * resource or its children.
+ */
+ public boolean isManaged(IResource resource) throws TeamException {
+
+ if (resource.equals(project))
+ return true;
+
+ // Ensure that the resource is a child of our project
+ if (!isChildResource(resource))
+ // Is returning false enough or should we throw an exception
+ return false;
+
+ // Get the IManagedResource corresponding to the resource and check if its managed
+ return getChild(resource).isManaged();
+ }
+
+ /**
+ * @see ITeamSynch#isOutOfDate(IResource)
+ */
+ public boolean isOutOfDate(IResource resource) {
+ // NOTE: For now, we'll just say we aren't but we'll need to fix this
+ return false;
+ }
+
+ /**
+ * Import a project into a CVS repository and then check out a local copy.
+ *
+ * Consideration: What if the project already exists?
+ *
+ * The supported properties are:
+ * connection The connection method to be used
+ * user The username for the connection
+ * password The password used for the connection (optional)
+ * host The host where the repository resides
+ * port The port to connect to (optional)
+ * root The server directory where the repository is located
+ * message The message to be attached (optional)
+ * vendor The vendor tag (optional)
+ * tag The version tag (optional)
+ */
+ public static void importAndCheckoutProject(IProject project, Properties configuration, IProgressMonitor monitor) throws TeamException {
+ CVSRepositoryLocation location = buildRepository(configuration);
+ // Get the location of the workspace root
+ IManagedFolder root = Client.getManagedFolder(project.getLocation().toFile());
+
+ // Create the meta-file
+ ProjectDescriptionManager.writeProjectDescription(project, monitor);
+
+ // Get the message
+ String message = configuration.getProperty("message");
+ if (message == null)
+ message = Policy.bind("CVSTeamProvider.initialImport");
+
+ // Get the vendor
+ String vendor = configuration.getProperty("vendor");
+ if (vendor == null)
+ vendor = location.getUsername();
+
+ // Get the vendor
+ String tag = configuration.getProperty("tag");
+ if (tag == null)
+ tag = "start";
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add(Client.MESSAGE_OPTION);
+ localOptions.add(message);
+ // Create filters for all known text files
+ String[] patterns = getBinaryFilePatterns(project);
+ for (int i=0;i<patterns.length;i++) {
+ localOptions.add(Client.WRAPPER_OPTION);
+ localOptions.add(patterns[i] + " -k 'b'");
+ }
+
+ // Perform a import
+ Client.execute(
+ Client.IMPORT,
+ new String[] {},
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{configuration.getProperty("module"), vendor, tag},
+ root,
+ monitor,
+ getPrintStream(),
+ location,
+ null);
+
+ // NOTE: we should check to see the results of the import
+
+ // perform the checkout
+ checkout(location, project, configuration.getProperty("module"), configuration.getProperty("tag"), monitor);
+ }
+
+ /**
+ * @see ITeamProvider#move(IResource, IPath, IProgressMonitor)
+ */
+ public void moved(IPath source, IResource resource, IProgressMonitor progress)
+ throws TeamException {
+
+ // this translates to a delete and an add
+
+ // How is this managed? Do we do the move or is that done after?
+ // It becomes complicated if the local and remote operations
+ // are independant as this is not the way CVS works!
+
+ // Could implement a CVSProvider.MOVE!!!
+
+ Client.execute(
+ Client.REMOVE,
+ new String[0],
+ new String[0],
+ new String[] {source.removeFirstSegments(1).toString()},
+ managedProject,
+ progress,
+ getPrintStream());
+ Client.execute(
+ Client.ADD,
+ new String[0],
+ new String[0], // We'll need to copy options from old entry
+ new String[] {resource.getFullPath().removeFirstSegments(1).toString()},
+ managedProject,
+ progress,
+ getPrintStream());
+ }
+
+ /**
+ * Set the comment to be used on the next checkin
+ */
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Set the connection method for the given resource's
+ * project. If the conection method name is invalid (i.e.
+ * no corresponding registered connection method), false is returned.
+ */
+ public boolean setConnectionInfo(IResource resource, String methodName, IUserInfo userInfo) throws TeamException {
+ checkIsChild(resource);
+ if (!CVSRepositoryLocation.validateConnectionMethod(methodName))
+ return false;
+ CVSRepositoryLocation location;
+ try {
+ location = ((CVSRepositoryLocation)userInfo);
+ } catch (ClassCastException e) {
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.invalidUserInfo"), null));
+ }
+ location.setUserMuteable(false);
+ location.updateCache();
+ location.setMethod(methodName);
+ final String root = location.getLocation();
+ managedProject.accept(new IManagedVisitor() {
+ public void visitFile(IManagedFile file) throws CVSException {};
+ public void visitFolder(IManagedFolder folder) throws CVSException {
+ FolderProperties info = folder.getFolderInfo();
+ info.setRoot(root);
+ folder.setFolderInfo(info);
+ folder.acceptChildren(this);
+ };
+ });
+ return true;
+ }
+
+ /**
+ * Ste the stream to which CVS command output is sent
+ */
+ public static void setPrintStream(PrintStream out) {
+ printStream = out;
+ }
+
+ /**
+ * Sets the userinfo (username and password) for the resource's project.
+ */
+ public void setUserInfo(IResource resource, IUserInfo userinfo) throws TeamException {
+ checkIsChild(resource);
+ try {
+ ((CVSRepositoryLocation)userinfo).updateCache();
+ } catch (ClassCastException e) {
+ throw new TeamException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.invalidUserInfo"), null));
+ }
+ }
+
+ /**
+ * @see ITeamProvider#refreshState(IResource[], int, IProgressMonitor)
+ */
+ public void refreshState(
+ IResource[] resources,
+ int depth,
+ IProgressMonitor progress)
+ throws TeamException {
+
+ // How does this translate to CVS?
+ // NIK: maybe an simple update ?
+ }
+
+ /*
+ * Refresh the affected resources after a CVSException occured.
+ * Initially we'll ignore any CoreException and throw the CVSException
+ * after the refresh
+ */
+ private void refreshResources(IResource[] resources, int depth, CVSException e, IProgressMonitor progress) throws CVSException {
+ CVSException newException = e;
+ try {
+ refreshResources(resources, depth, progress);
+ } catch (CoreException coreException) {
+ // Only use the new exception if there was no old one
+ if (newException == null)
+ newException = new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, Policy.bind("CVSTeamProvider.refreshError", new Object[] {project.getFullPath().toString()}), coreException));
+ }
+ if (newException != null)
+ throw newException;
+ }
+ private IResource[] allChildrenOf(IResource[] resources, int depth, IProgressMonitor progress) throws CoreException {
+ final List allResources = new ArrayList();
+ for (int i=0;i<resources.length;i++) {
+ resources[i].accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ allResources.add(resource);
+ return true;
+ }
+ }, depth, false);
+ }
+ return (IResource[])allResources.toArray(new IResource[allResources.size()]);
+ }
+ private void refreshResources(IResource[] resources, int depth, IProgressMonitor progress) throws CoreException {
+ // NOTE: We may not catch all resources changes in this way
+ for (int i = 0; i < resources.length; i++) {
+ IResource r = resources[i];
+ r.refreshLocal(depth, progress);
+ }
+ // NOTE: We need to refresh based on the depth
+ // We should try to be smart by getting the results from the command
+ TeamPlugin.getManager().broadcastResourceStateChanges(resources);
+ }
+
+ /**
+ * Tag the resources in the CVS repository with the given tag.
+ */
+ public void tag(IResource[] resources, int depth, String tag, boolean isBranch, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, depth, progress);
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ // If the depth is not infinite, we want the -l option
+ if (depth != IResource.DEPTH_INFINITE)
+ localOptions.add(Client.LOCAL_OPTION);
+ if (isBranch)
+ localOptions.add(Client.BRANCH_OPTION);
+
+ // The tag name is supposed to be the first argument
+ ArrayList args = new ArrayList();
+ args.add(tag);
+ args.addAll(Arrays.asList(arguments));
+ arguments = (String[])args.toArray(new String[args.size()]);
+
+ Client.execute(
+ Client.TAG,
+ new String[] {},
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ arguments,
+ managedProject,
+ progress,
+ getPrintStream());
+ }
+
+ /**
+ * Currently, we support only the optimistic model so uncheckout dores nothing.
+ *
+ * @see ITeamProvider#uncheckout(IResource[], int, IProgressMonitor)
+ */
+ public void uncheckout(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+ }
+
+ /**
+ * Generally usefull update
+ */
+ public void update(IResource[] resources, int depth, IProgressMonitor progress) throws TeamException {
+ // Build the local options
+ List localOptions = new ArrayList();
+ if (depth == IResource.DEPTH_INFINITE) {
+ // if depth = infinite, look for new directories
+ localOptions.add(Client.DEEP_OPTION);
+ // For now, prune empty directories
+ // This must be done by the client! (not the server)
+ localOptions.add(Client.PRUNE_OPTION);
+ }
+ else
+ // If depth = zero or 1, use -l
+ localOptions.add(Client.LOCAL_OPTION);
+ update(resources, depth, (String[])localOptions.toArray(new String[localOptions.size()]), progress);
+
+ }
+ /*
+ * CVS specific update
+ */
+ private void update(IResource[] resources, int depth, String[] localOptions, IProgressMonitor progress) throws TeamException {
+
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, depth, progress);
+
+ try {
+ Client.execute(
+ Client.UPDATE,
+ DEFAULT_GLOBAL_OPTIONS,
+ localOptions,
+ arguments,
+ managedProject,
+ progress,
+ getPrintStream());
+ } catch(CVSException e) {
+ refreshResources(resources, depth, e, progress);
+ }
+ refreshResources(resources, depth, null, progress);
+ }
+
+ private static TeamException wrapException(CoreException e) {
+ return new TeamException(statusFor(e));
+ }
+
+ public static TeamException wrapException(CVSException e, List errors) {
+ // NOTE: Need to find out how to pass MultiStatus. Is it up to me to subclass?
+ return e;
+ }
+
+ private static IStatus statusFor(CoreException e) {
+ // We should be taking out any status from the CVSException
+ // and creating an array of IStatus!
+ return new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE, getMessageFor(e), e);
+ }
+
+ public static String getMessageFor(Exception e) {
+ String message = Policy.bind(e.getClass().getName(), new Object[] {e.getMessage()});
+ if (message.equals(e.getClass().getName()))
+ message = Policy.bind("CVSTeamProvider.exception", new Object[] {e.toString()});
+ return message;
+ }
+
+ /**
+ * Cause a snapshot (this saves the sync info to disk)
+ */
+ static void snapshot(IProgressMonitor monitor) throws CoreException {
+ monitor = Policy.monitorFor(monitor);
+ monitor.subTask(Policy.bind("CVSTeamProvider.snapshot"));
+ ResourcesPlugin.getWorkspace().save(false, monitor);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Client.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Client.java
new file mode 100644
index 000000000..47949c506
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Client.java
@@ -0,0 +1,385 @@
+package org.eclipse.team.internal.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.commands.CommandDispatcher;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.ResourceFactory;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.response.IResponseHandler;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * An generic cvs client that can execute a request and can handle
+ * responses. It is called like the according to the specification
+ * of cvs-command line clients. You have to give the execute method
+ * the command, all the parameters, a stream to pipe the messages from
+ * the server to and a monitor.<br>
+ * After the client has established a connection to a server it
+ * uses the client / server negotiation protcol to tell the server
+ * which response can be handled by the client. Additionally the
+ * server tells the client which requests can the handle by the
+ * server. The list of responses that can be handled by the client
+ * is determined by the registered handlers.<br>
+ * <p>
+ * Although not documented, the client must have a handler for at
+ * least the following responses: "ok", "error", "Checked-in",
+ * "Updated", "Merged", "Removed", "M" text and "E" text.
+ * <p>
+ * The client installs handlers for all must responses.
+ */
+public class Client {
+
+ public static final String CURRENT_LOCAL_FOLDER = ".";
+ public static final String CURRENT_REMOTE_FOLDER = "";
+ public static final String SERVER_SEPARATOR = "/";
+
+ public static final String CHECKOUT = "co";
+ public static final String UPDATE = "update";
+ public static final String COMMIT = "ci";
+ public static final String ADD = "add";
+ public static final String REMOVE = "remove";
+ public static final String IMPORT = "import";
+ public static final String TAG = "tag";
+ public static final String DIFF = "diff";
+ public static final String ADMIN = "admin";
+ public static final String STATUS = "status";
+ public static final String LOG = "log";
+
+ // Global Options
+ public static final String REPO_OPTION = "-d";
+ public static final String NOCHANGE_OPTION = "-n";
+
+ // Local Options
+ public static final String IGNORE_OPTION = "-I";
+ public static final String WRAPPER_OPTION = "-W";
+ public static final String KB_OPTION = "-kb";
+ public static final String KO_OPTION = "-ko";
+ public static final String PRUNE_OPTION = "-P";
+ public static final String TAG_OPTION = "-r";
+ public static final String BRANCH_OPTION = "-b";
+ public static final String DEEP_OPTION = "-d";
+ public static final String IGNORE_LOCAL_OPTION = "-d";
+ public static final String LOCAL_OPTION = "-l";
+ public static final String MESSAGE_OPTION = "-m";
+
+ public static final String[] EMPTY_ARGS_LIST = new String[0];
+
+ /**
+ * Executes the given request
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder localFolder,
+ IProgressMonitor monitor,
+ PrintStream messageOut,
+ CVSRepositoryLocation repository,
+ IResponseHandler[] customHandlers)
+ throws CVSException {
+ Connection connection = repository.openConnection();
+ try {
+ execute(request, globalOptions, localOptions, arguments, localFolder, monitor, messageOut, connection, customHandlers);
+ } finally {
+ connection.close();
+ }
+ }
+
+ /**
+ * Executes the given request.
+ *
+ * create a new Connection to the server, either from an "-d" global option
+ * or from the Root-Properie of the root-folder.
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+
+ CVSRepositoryLocation repository;
+ Connection connection;
+
+ // this is a hack to prevent from changing the
+ // acctuall array that is given from the user
+ // while nulling the "-d" ":pserver:nkra@ ... " option
+ globalOptions = (String[]) globalOptions.clone();
+
+ repository = getRepository(globalOptions, mRoot);
+ connection = repository.openConnection();
+
+ try {
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ mRoot,
+ monitor,
+ messageOut,
+ connection);
+ } finally {
+ connection.close();
+ }
+
+ }
+
+ /**
+ * Executes the given request.
+ *
+ * Uses a connection to the server that is allready there. This
+ * expects you not to use the global option "-d".
+ *
+ * Will this ever close the connection? If so, what are the conditions
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut,
+ Connection connection)
+ throws CVSException {
+
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ mRoot,
+ monitor,
+ messageOut,
+ connection,
+ null);
+ }
+
+ /**
+ * Creates a new client connection for the given cvs repository location.
+ *
+ * Sets up the three main objects of the program:
+ *
+ * commandDispatcher => Knows about commands (update, commit ...)
+ * responseDispatcher => Reacts on input from the server
+ * requestSender => Knows how to send requests to the server
+ *
+ * @see commandDispatcher
+ * @see RequestSender
+ * @see responseDispatcher
+ *
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut,
+ Connection connection,
+ IResponseHandler[] customHandlers)
+ throws CVSException {
+
+ ResponseDispatcher responseDispatcher = new ResponseDispatcher(connection, customHandlers);
+ RequestSender requestSender = new RequestSender(connection);
+ CommandDispatcher commandDispatcher = new CommandDispatcher(responseDispatcher, requestSender);
+
+ initialize(responseDispatcher, requestSender, connection, mRoot, monitor, messageOut);
+
+ commandDispatcher.execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ mRoot,
+ monitor,
+ messageOut);
+ }
+
+ /**
+ * @see Client#(String,String[],String[],String[],ICVSFolder,IProgressMonitor,OutputStream,Connection)
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ ICVSFolder root,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ ResourceFactory.getManaged(root),
+ monitor,
+ messageOut);
+ }
+
+ /**
+ * @see Client#(String,String[],String[],String[],ICVSFolder,IProgressMonitor,OutputStream,Connection)
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ ICVSFolder root,
+ IProgressMonitor monitor,
+ PrintStream messageOut,
+ Connection connection)
+ throws CVSException {
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ ResourceFactory.getManaged(root),
+ monitor,
+ messageOut,
+ connection);
+ }
+
+ /**
+ * @see Client#(String,String[],String[],String[],ICVSFolder,IProgressMonitor,OutputStream,Connection)
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ File root,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ ResourceFactory.getManagedFolder(root),
+ monitor,
+ messageOut);
+ }
+
+ /**
+ * @see Client#(String,String[],String[],String[],ICVSFolder,IProgressMonitor,OutputStream,Connection)
+ */
+ public static void execute(String request,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ File root,
+ IProgressMonitor monitor,
+ PrintStream messageOut,
+ Connection connection)
+ throws CVSException {
+ execute(request,
+ globalOptions,
+ localOptions,
+ arguments,
+ ResourceFactory.getManagedFolder(root),
+ monitor,
+ messageOut,
+ connection);
+ }
+
+ /**
+ * Gives you an ManagedFolder for a absolut path in
+ * platform dependend style
+ *
+ * @throws CVSException on path.indexOf("CVS") != -1
+ * @throws CVSException on internal IOExeption
+ */
+ public static IManagedFolder getManagedFolder(String folder) throws CVSException {
+ return ResourceFactory.getManagedFolder(folder);
+ }
+ public static IManagedFolder getManagedFolder(File folder) throws CVSException {
+ return ResourceFactory.getManagedFolder(folder);
+ }
+ public static IManagedResource getManagedResource(File file) throws CVSException {
+ return ResourceFactory.getManaged(file);
+ }
+
+ /**
+ * Intializes the client.
+ *
+ * Gets the valid-requsts form the server, and puts them into the
+ * request sender.
+ *
+ * MV: Why isn't this method in the Command execute method?
+ */
+ private static void initialize(ResponseDispatcher responseDispatcher,
+ RequestSender requestSender,
+ Connection connection,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+
+ // Tell the server our response handlers.
+ connection.writeLine(requestSender.VALID_RESPONSES, responseDispatcher.makeResponseList());
+
+ // Get all valid requests from the server
+ connection.writeLine(requestSender.VALID_REQUESTS);
+
+ // Get the responseHandler that does put the valid-requests
+ // into the requestSender
+ IResponseHandler validRequestHandler = requestSender.getValidRequestHandler();
+
+ // Register the responseHandler, process the server-reply
+ // unregister it afterwards ... we are not going to get
+ // another response of this kind
+ responseDispatcher.registerResponseHandler(validRequestHandler);
+ responseDispatcher.manageResponse(monitor,mRoot,messageOut);
+ responseDispatcher.unregisterResponseHandler(validRequestHandler);
+
+ // Set the root.
+ // we just send it. If we do not send it we have got
+ // a problem anyway ... so we do not bother checking if it
+ // is allowed (we could do so with "requestSender.isValidRequest(ROOT)"
+ connection.writeLine(requestSender.ROOT, connection.getRootDirectory());
+ }
+
+ /**
+ * This give you a new repo either from the global "-d" option
+ * or form the root-property in the folder.
+ *
+ * This has to be rewritten in a nicer style.
+ */
+ private static CVSRepositoryLocation getRepository(String[] globalOptions,
+ IManagedFolder mFolder)
+ throws CVSException {
+
+ String repoName = null;
+
+ Assert.isNotNull(mFolder);
+
+ // look if the repo is specified in the global Options
+ // this delets the option as well which is not so beatyful, but
+ // we have got a copy and we do not want this option to appear
+ // any more
+ repoName = Util.getOption(globalOptions,REPO_OPTION,true);
+
+ // look if we have got an root-entrie in the root-folder
+ if (repoName == null && mFolder.exists() && mFolder.isCVSFolder()) {
+ repoName = mFolder.getFolderInfo().getRoot();
+ }
+
+ if (repoName == null) {
+ throw new CVSException("CVSROOT is not specified");
+ }
+
+ return CVSRepositoryLocation.fromString(repoName);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSRepositoryLocation.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSRepositoryLocation.java
new file mode 100644
index 000000000..753f8f026
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ICVSRepositoryLocation.java
@@ -0,0 +1,71 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * This interface provides access to the specific portions of
+ * the repository location string for use by connection methods
+ * and the user authenticator.
+ *
+ * It is not intended to implemented by clients.
+ *
+ * @see IUserAuthenticator
+ * @see IConnectionMethod
+ */
+public interface ICVSRepositoryLocation {
+
+ /**
+ * port value which indicates to a connection method to use the default port
+ */
+ public static int USE_DEFAULT_PORT = 0;
+
+ /**
+ * Return the connection method for making the connection
+ */
+ public IConnectionMethod getMethod();
+
+ /**
+ * Returns the host where the repository is located
+ */
+ public String getHost();
+
+ /**
+ * Returns the port to connect to or USE_DEFAULT_PORT if
+ * the connection method is to use its default port.
+ */
+ public int getPort();
+
+ /**
+ * Returns the root directory of the repository.
+ */
+ public String getRootDirectory();
+
+ /**
+ * Returns the string representing the receiver. This string
+ * should contain enough information to recreate the receiver.
+ */
+ public String getLocation();
+
+ /**
+ * Return the conection timeout value in milliseconds.
+ * A value of 0 means there is no timeout value.
+ */
+ public int getTimeout();
+
+ /**
+ * Return the information about the user as an IUserInfo.
+ *
+ * This allows the querying of the user name and the setting
+ * of the username and password.
+ */
+ public IUserInfo getUserInfo();
+
+ /**
+ * Return the username
+ */
+ public String getUsername();
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IConnectionMethod.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IConnectionMethod.java
new file mode 100644
index 000000000..81d122db8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IConnectionMethod.java
@@ -0,0 +1,31 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Implementators of this class can act as factories for creating connections to a CVS server
+ * with the desired custom communication protocol. Providers of CVS connection methods must implement
+ * this interface and register the implementation with the extension point:
+ *
+ * org.eclipse.team.cvs.core.connectionmethods
+ *
+ * The <code>createConnection()</code> method will be invoked by the CVS client when the user
+ * is attempting to make a connection to the server using the connection name which matches
+ * the <code>String</code> returned by <code>getName()</code> (e.g. "pserver", "ext", etc.).
+ */
+public interface IConnectionMethod {
+
+ /**
+ * Returns the name of this connection method (e.g."local", "ext").
+ */
+ public String getName();
+
+ /**
+ * Creates a new server connection using the given repository root
+ * (which includes the user name) and the given password.
+ */
+ public IServerConnection createConnection(ICVSRepositoryLocation location, String password);
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ILogEntry.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ILogEntry.java
new file mode 100644
index 000000000..6b64d8ae6
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/ILogEntry.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Instances of ILogEntry represent an entry for a CVS file that results
+ * from the cvs log command.
+ *
+ * Clients are not expected to implement this interface
+ */
+public interface ILogEntry {
+
+ /**
+ * Get the revision for the entry
+ */
+ public String getRevision();
+
+ /**
+ * Get the author of the revision
+ */
+ public String getAuthor();
+
+ /**
+ * Get the date the revision was committed
+ */
+ public String getDate();
+
+ /**
+ * Get the comment for the revision
+ */
+ public String getComment();
+
+ /**
+ * Get the state
+ */
+ public String getState();
+
+ /**
+ * Get the tags associated with the revision
+ */
+ public String[] getTags();
+
+ /**
+ * Get the remote file for this entry
+ */
+ public IRemoteFile getRemoteFile();
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IServerConnection.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IServerConnection.java
new file mode 100644
index 000000000..bef5d404b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IServerConnection.java
@@ -0,0 +1 @@
+package org.eclipse.team.ccvs.core; /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.eclipse.team.internal.ccvs.core.connection.CVSAuthenticationException; /** * CVS supports different connection methods for communicating between a client and the server. * Furthermore, custom connection methods can be added. Connection methods are added * to the CVS client as an IConnectionMethod, which can be used to create connections of * type IServerConnection. * * @see IConnectionMethod */ public interface IServerConnection { /** * Open a connection to the CVS server. * * Throw CVSAuthenticationException if the username or password is invalid. * Throw IOExceptions for other failures. */ public void open() throws IOException, CVSAuthenticationException; /** * Close the connection * * Throw IOException on failures */ public void close() throws IOException; /** * Get the input stream to receive responses from the server */ public InputStream getInputStream(); /** * Get the output stream to send requests to the server */ public OutputStream getOutputStream(); } \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserAuthenticator.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserAuthenticator.java
new file mode 100644
index 000000000..f443b03c9
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserAuthenticator.java
@@ -0,0 +1,64 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * IUserAuthenticators are used to ensure that the user
+ * is validated for access to a given repository. The
+ * user is prompted for a username and password as
+ * appropriate for the given repository type.
+ */
+public interface IUserAuthenticator {
+ /**
+ * Authenticates the user for access to a given repository.
+ * The obtained values for user name and password will be placed
+ * into the supplied user info object. Implementors are allowed to
+ * save user names and passwords. The user should be prompted for
+ * user name and password if there is no saved one, or if <code>retry</code>
+ * is <code>true</code>.
+ *
+ * @param location The repository location to authenticate the user for.
+ * @param info The object to place user validation information into.
+ * @param retry <code>true</code> if a previous attempt to log in failed.
+ * @param message An optional message to display if, e.g., previous authentication failed.
+ * @return true if the validation was successful, and false otherwise.
+ */
+ public boolean authenticateUser(ICVSRepositoryLocation location, IUserInfo userInfo, boolean retry, String message) throws CVSException;
+
+ /**
+ * Store the password for the given repository location. The password of the provided IUserInfo
+ * is also set by this operation.
+ *
+ * @param location The repository location asspociated with the given username and password.
+ * @param userinfo The userinfo object containing the username
+ * @param password The password to be stored
+ *
+ * @exception CVSException if there are problems caching the authorization information.
+ */
+ public void cachePassword(ICVSRepositoryLocation location, IUserInfo userInfo, String password) throws CVSException;
+
+ /**
+ * Retrieve the username and password for the given repository location into the provided IUserInfo object.
+ *
+ * @param location The repository location asspociated with the given username and password.
+ * @param userinfo The userinfo object effected
+ * @return <code>true</code> if the username and password were set
+ *
+ * @exception CVSException if there are problems retrieving the authorization information.
+ */
+ public boolean retrievePassword(ICVSRepositoryLocation location, IUserInfo userInfo) throws CVSException;
+
+ /**
+ * Dispose of any information associated with the given location.
+ *
+ * @param location The repository location being disposed.
+ *
+ * @exception CVSException if there are problems removing the authorization information.
+ */
+ public void dispose(ICVSRepositoryLocation location) throws CVSException;
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserInfo.java
new file mode 100644
index 000000000..6725306a8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/IUserInfo.java
@@ -0,0 +1,34 @@
+package org.eclipse.team.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Instances of this class represent a username password pair.
+ * Both values can be set and the username can be retrieved.
+ * However, it is possible that the username is not mutable.
+ * Users must check before trying to set the username.
+ *
+ * Clients are not expected to implement this interface
+ */
+public interface IUserInfo {
+ /**
+ * Get the username for this user.
+ */
+ public String getUsername();
+ /**
+ * Return true if the username is mutable. If not, setUsername should not be called.
+ */
+ public boolean isUsernameMutable();
+ /**
+ * Sets the password for this user.
+ */
+ public void setPassword(String password);
+ /**
+ * Sets the username for this user. This should not be called if
+ * isUsernameMutable() returns false.
+ */
+ public void setUsername(String username);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java
new file mode 100644
index 000000000..cfd9b2cdb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/Policy.java
@@ -0,0 +1,98 @@
+package org.eclipse.team.internal.ccvs.core;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.MessageFormat;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.SubProgressMonitor;
+
+public class Policy {
+ protected static ResourceBundle bundle = null;
+
+ /**
+ * Creates a NLS catalog for the given locale.
+ */
+ public static void localize(String bundleName) {
+ bundle = ResourceBundle.getBundle(bundleName);
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given string.
+ */
+ public static String bind(String id, String binding) {
+ return bind(id, new String[] { binding });
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given strings.
+ */
+ public static String bind(String id, String binding1, String binding2) {
+ return bind(id, new String[] { binding1, binding2 });
+ }
+
+ /**
+ * Gets a string from the resource bundle. We don't want to crash because of a missing String.
+ * Returns the key if not found.
+ */
+ public static String bind(String key) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and binds it with the given arguments. If the key is
+ * not found, return the key.
+ */
+ public static String bind(String key, Object[] args) {
+ try {
+ return MessageFormat.format(bind(key), args);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Progress monitor helpers
+ */
+ public static void checkCanceled(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ }
+ public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ return monitor;
+ }
+
+ public static IProgressMonitor subMonitorFor(IProgressMonitor monitor, int ticks) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ if (monitor instanceof NullProgressMonitor)
+ return monitor;
+ return new SubProgressMonitor(monitor, ticks);
+ }
+ public static IProgressMonitor subMonitorFor(IProgressMonitor monitor, int ticks, int style) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ if (monitor instanceof NullProgressMonitor)
+ return monitor;
+ return new SubProgressMonitor(monitor, ticks, style);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractMessageCommand.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractMessageCommand.java
new file mode 100644
index 000000000..9119380d1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractMessageCommand.java
@@ -0,0 +1,55 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+/**
+ * Superclass for commands that do not change the structure on
+ * the local working copy (it can change the content of the files).<br>
+ * Most of the subclasses are asking the server for response in
+ * message format (log, status)
+ */
+abstract class AbstractMessageCommand extends Command {
+
+ /**
+ * Constructor for AbstractMessageCommand.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public AbstractMessageCommand(
+ ResponseDispatcher responseDispatcher,
+ RequestSender requestSender) {
+ super(responseDispatcher, requestSender);
+ }
+
+ /**
+ * @see Command#sendRequestsToServer(IProgressMonitor)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor)
+ throws CVSException {
+
+ IManagedResource[] mWorkResources;
+
+ // NOTE: We could save ourselves a bit if work by getting
+ // the resources first and passing them as arguments
+ Assert.isTrue(allResourcesManaged());
+
+ // Get the folders we want to work on
+ mWorkResources = getWorkResources();
+
+ // Send all folders that are already managed to the server
+ sendFileStructure(mWorkResources,monitor,false,false,false);
+ sendHomeFolder();
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractStructureVisitor.java
new file mode 100644
index 000000000..c636c5f52
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AbstractStructureVisitor.java
@@ -0,0 +1,105 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.FileDescriptor;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+
+/**
+ * An IManagedVisitor that is superclass to all IManagedVisitor's used
+ * by Command and it's subclasses.
+ * Provides helper methods to send files and folders with modifications
+ * to the server.
+ */
+abstract class AbstractStructureVisitor implements IManagedVisitor {
+
+ private final RequestSender requestSender;
+ private final IManagedFolder mRoot;
+ private final IProgressMonitor monitor;
+ //The last folder that has already been sent to the server during this visit
+ private IManagedFolder lastFolderSend;
+
+ public AbstractStructureVisitor(RequestSender requestSender,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor) {
+
+ this.requestSender = requestSender;
+ this.mRoot = mRoot;
+ this.monitor = monitor;
+ }
+
+ /**
+ * Send the folder relative to the root to the server
+ */
+ void sendFolder(IManagedFolder mFolder,
+ boolean contructFolder)
+ throws CVSException{
+
+ String local;
+ String remote;
+
+ // Do not send the same folder twice
+ if (mFolder.equals(lastFolderSend)) {
+ return;
+ }
+
+ local = mFolder.getRelativePath(mRoot);
+
+ if (contructFolder && mFolder.exists()) {
+ requestSender.sendConstructedDirectory(local,local);
+ lastFolderSend = mFolder;
+ return;
+ }
+
+ remote = mFolder.getRemoteLocation(mRoot);
+
+ if (remote != null) {
+ requestSender.sendDirectory(local, remote);
+ }
+
+ // Remember, that we send this folder
+ lastFolderSend = mFolder;
+ }
+
+ /**
+ * Send a file up to the server.
+ * If it is modified send the content as well.
+ */
+ void sendFile(IManagedFile mFile,
+ boolean sendQuestionable,
+ String mode) throws CVSException {
+
+ boolean binary = mode!=null &&
+ mode.indexOf(FileProperties.BINARY_TAG)!=-1;
+
+ if (mFile.isManaged()) {
+ requestSender.sendEntry(mFile.getFileInfo().getEntryLineForServer());
+ } else if (sendQuestionable) {
+ requestSender.sendQuestionable(mFile.getName());
+ return;
+ // The client does not do it and we do not know whether to do it
+ // } else if (mode != null && !"".equals(mode)) {
+ // requestSender.sendKopt(mode);
+ }
+
+ if (!mFile.exists()) {
+ return;
+ }
+
+ if (mFile.isDirty()) {
+ requestSender.sendModified(mFile,monitor,binary);
+ } else {
+ requestSender.sendUnchanged(mFile.getName());
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Add.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Add.java
new file mode 100644
index 000000000..916ffc7d1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Add.java
@@ -0,0 +1,123 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Add extends Command {
+
+ /**
+ * Constructor for Add.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Add(ResponseDispatcher responseContainer, RequestSender requestSender) {
+ super(responseContainer, requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.ADD;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.ADD;
+ }
+
+ /**
+ * Checks wether all the arguments (that are meand as Files/Folders)
+ * to add, can give a remoteLocation (that is needed to add them)
+ */
+ protected boolean canTraverse() {
+
+ IManagedResource[] mWorkResources;
+
+ try {
+ mWorkResources = getWorkResources();
+
+ for (int i=0; i<mWorkResources.length; i++) {
+ Assert.isNotNull(mWorkResources[i].getRemoteLocation(getRoot()));
+ }
+ } catch (CVSException e) {
+ Assert.isTrue(false);
+ }
+
+ return true;
+ }
+
+ /**
+ * @see Command#sendRequestsToServer(IProgressMonitor)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor) throws CVSException {
+
+ IManagedResource[] mWorkResources;
+ IManagedVisitor vistor;
+
+ Assert.isTrue(getArguments().length != 0);
+
+ // Check that all the arguments can give you an
+ // repo that you will need while traversing the
+ // file-structure
+ Assert.isTrue(canTraverse());
+
+ // Get a vistor and use it on every resource we should
+ // work on
+ vistor = new AddStructureVisitor(requestSender,getRoot(),monitor);
+ mWorkResources = getWorkResources();
+ for (int i = 0; i < mWorkResources.length; i++) {
+ mWorkResources[i].accept(vistor);
+ }
+
+ sendHomeFolder();
+ }
+
+ /**
+ * If we were successful in adding, then acctually managed
+ * the folders on disk
+ */
+ protected void finished(boolean succsess) throws CVSException {
+
+ IManagedFolder mFolder;
+ IManagedResource[] mWorkResources;
+ FolderProperties folderInfo;
+
+ mWorkResources = getWorkResources();
+
+ if (!succsess) {
+ return;
+ }
+
+ for (int i=0; i<mWorkResources.length; i++) {
+ if (mWorkResources[i].isFolder()) {
+
+ mFolder = (IManagedFolder) mWorkResources[i];
+
+ folderInfo = mFolder.getParent().getFolderInfo();
+ folderInfo.setRepository(folderInfo.getRepository() +
+ Client.SERVER_SEPARATOR + mFolder.getName());
+ mFolder.setFolderInfo(folderInfo);
+
+ }
+ }
+
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AddStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AddStructureVisitor.java
new file mode 100644
index 000000000..86a46476a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/AddStructureVisitor.java
@@ -0,0 +1,92 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * This is a visitor that is specially created for the add-command.<br>
+ * It traverses the file-structure in the other direction, so that
+ * all the parents are send until a parent is found that should allready
+ * be known by the to the root are send.<br>
+ * The visitor remembers the folders it has allready been to and does not
+ * send them again (if possible).
+ */
+public class AddStructureVisitor extends AbstractStructureVisitor {
+
+ private boolean forceSend = false;
+ private Set visitedFolders = new HashSet();
+ private IManagedFolder lastVisitedFolder;
+ private IManagedFolder mRoot;
+ private RequestSender requestSender;
+
+ /**
+ * Constructor for AddStructureVisitor.
+ * @param requestSender
+ * @param mRoot
+ * @param monitor
+ */
+ public AddStructureVisitor(
+ RequestSender requestSender,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor) {
+ super(requestSender, mRoot, monitor);
+ this.mRoot = mRoot;
+ this.requestSender = requestSender;
+ }
+
+ /**
+ * @see IManagedVisitor#visitFile(IManagedFile)
+ */
+ public void visitFile(IManagedFile mFile) throws CVSException {
+
+ if (!mFile.getParent().equals(lastVisitedFolder)) {
+ forceSend = true;
+ mFile.getParent().accept(this);
+ }
+
+ // We just send the fact, that the file is modified
+ // not the data, we do not need it.
+ requestSender.sendIsModified(mFile.getName());
+
+ }
+
+ /**
+ * @see IManagedVisitor#visitFolder(IManagedFolder)
+ */
+ public void visitFolder(IManagedFolder mFolder) throws CVSException {
+
+ Assert.isNotNull(mFolder);
+
+ // Save the status wheter we want to send
+ // this folder in every case
+ boolean alreadyVisited;
+ boolean forceSend = this.forceSend;
+ this.forceSend = false;
+
+ alreadyVisited = visitedFolders.contains(mFolder);
+
+ if (!mFolder.equals(mRoot) && !alreadyVisited) {
+ mFolder.getParent().accept(this);
+ }
+
+ if (forceSend || !alreadyVisited) {
+ visitedFolders.add(mFolder);
+ lastVisitedFolder = mFolder;
+ sendFolder(mFolder,false);
+ }
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Admin.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Admin.java
new file mode 100644
index 000000000..69baa6b7f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Admin.java
@@ -0,0 +1,39 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+public class Admin extends AbstractMessageCommand {
+
+ /**
+ * Constructor for Admin.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Admin(
+ ResponseDispatcher responseContainer,
+ RequestSender requestSender) {
+ super(responseContainer, requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.ADMIN;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.ADMIN;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Checkout.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Checkout.java
new file mode 100644
index 000000000..30a529ccc
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Checkout.java
@@ -0,0 +1,60 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Checkout extends Command {
+
+ /**
+ * Pipe everything to the superclass
+ */
+ public Checkout(ResponseDispatcher responseDispathcer,
+ RequestSender requestSender) {
+ super(responseDispathcer,requestSender);
+ }
+
+ /**
+ * @see Request#getName()
+ */
+ public String getName() {
+ return RequestSender.CHECKOUT;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.CHECKOUT;
+ }
+
+ /**
+ * Start the Checkout command:
+ * Send the module that is going to be checked-out to the server
+ * by reading the name of the resource given
+ * (This has to change to we give it the name of the modul and the
+ * Checkout creates everything for us)
+ *
+ *
+ * @see Request#setUp(IRequestContext)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor) throws CVSException {
+
+ // We need a folder to put the project(s) we checkout into
+ Assert.isTrue(getRoot().isFolder());
+
+ // Just send the homefolder, and do not look into
+ // the CVS-Folder to send it to the server
+ // (this could be changed to make it compatible)
+ sendHomeFolder(false);
+
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Command.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Command.java
new file mode 100644
index 000000000..4300d1f04
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Command.java
@@ -0,0 +1,378 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * Abstract base class for the commands which implements the ICommand
+ * interface so subclasses can be added to the CommandDispatcher.
+ *
+ * Also you do not need to use this class to implement commands
+ * because the dispatcher makes use of ICommand only. However, all
+ * the current command are derived from this class.
+ */
+abstract class Command implements ICommand {
+
+ private String[] globalOptions;
+ private String[] localOptions;
+ private String[] arguments;
+
+ private IManagedFolder mRoot;
+
+ protected final ResponseDispatcher responseDispatcher;
+ protected final RequestSender requestSender;
+
+ /**
+ * The CommandDispatcher, the ResponseDispatcher and
+ * the RequestSender are the three major objects in
+ * the client.
+ *
+ * ResponseDispatcher is used to process the response form the server.
+ * RequestSender is used to send requests to the server.
+ */
+ public Command(ResponseDispatcher responseDispatcher,
+ RequestSender requestSender) {
+
+ this.responseDispatcher = responseDispatcher;
+ this.requestSender = requestSender;
+ }
+
+ /**
+ * Execute the given command. Do so by invoking the sendRequestsToServer method.
+ * Does handle the work with the progress-monitor.
+ *
+ * @see ICommand#execute(Connection, String[], String[], ICVSResource, OutputStream)
+ */
+ public void execute (
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+
+ // Record the arguments so subclass can access them using the get methods
+ this.mRoot = mRoot;
+ this.globalOptions = globalOptions;
+ this.localOptions = localOptions;
+ this.arguments = arguments;
+
+ try {
+
+ monitor.beginTask(Policy.bind("Command.server"), 100);
+ Policy.checkCanceled(monitor);
+
+ // Send the options to the server (the command itself has to care
+ // about the arguments)
+ // It is questionable if this is going to stay here, because
+ // NOTE: because why?
+ sendGlobalOptions();
+ sendLocalOptions();
+
+ // Guess that set up contributes 20% of work.
+ sendRequestsToServer(Policy.subMonitorFor(monitor, 20));
+ Policy.checkCanceled(monitor);
+
+ // Send all arguments to the server
+ sendArguments();
+ // Send the request name to the server
+ requestSender.writeLine(getRequestName());
+
+ try {
+ // Processing responses contributes 70% of work.
+ responseDispatcher.manageResponse(Policy.subMonitorFor(monitor, 70), mRoot, messageOut);
+
+ } catch (CVSException e) {
+ finished(false);
+ throw e;
+ }
+ // Finished adds last 10% of work.
+ finished(true);
+ monitor.worked(10);
+ } finally {
+ monitor.done();
+ }
+
+ }
+
+ /**
+ * Abstract method to send the complete arguments of the command to the server.
+ * The command itself is not sent here but in the execute method.
+ */
+ protected abstract void sendRequestsToServer(IProgressMonitor monitor) throws CVSException;
+
+ /**
+ * Called after command has been executed to allow subclasses to cleanup.
+ * Default is to do nothing.
+ */
+ protected void finished(boolean success) throws CVSException {
+ }
+
+ /**
+ * Sends the arguments to the server.
+ */
+ protected void sendArguments() throws CVSException {
+ if (arguments == null) {
+ return;
+ }
+ for (int i= 0; i < arguments.length; i++) {
+ requestSender.sendArgument(arguments[i]);
+ }
+ }
+
+ /**
+ * Sends localOptions to the server.
+ */
+ protected void sendLocalOptions() throws CVSException {
+ if (localOptions == null)
+ return;
+ for (int i= 0; i < localOptions.length; i++) {
+ requestSender.sendArgument(localOptions[i]);
+ }
+ }
+
+ /**
+ * Sends the global options to the server.
+ *
+ * It is allowed for the globalOptions to have null-values so this
+ * method has to cope with null-values in the array. Also, the
+ * global options may be null at all.
+ */
+ protected void sendGlobalOptions() throws CVSException {
+ if (globalOptions == null) {
+ return;
+ }
+ for (int i= 0; i < globalOptions.length; i++) {
+ if (globalOptions[i] != null) {
+ requestSender.sendGlobalOption(globalOptions[i]);
+ }
+ }
+ }
+
+ /**
+ * Send the homefolder as last thing before you send (eventually the
+ * arguments and then) the command.
+ *
+ * lookLocal specifies whether the system tries to look into the
+ * CVS properties for the folder.
+ */
+ protected void sendHomeFolder(boolean lookLocal) throws CVSException {
+ if (lookLocal && mRoot.isCVSFolder()) {
+ requestSender.sendDirectory(Client.CURRENT_LOCAL_FOLDER, mRoot.getRemoteLocation(mRoot));
+ } else {
+ requestSender.sendConstructedDirectory(Client.CURRENT_LOCAL_FOLDER, Client.CURRENT_REMOTE_FOLDER);
+ }
+ }
+
+ /**
+ * Send the homefolder as last thing before you send (eventually the
+ * arguments and then) the command
+ */
+ protected void sendHomeFolder() throws CVSException {
+ sendHomeFolder(true);
+ }
+
+ /**
+ * Gets the getGlobalOptions
+ * @return Returns a String[]
+ */
+ protected String[] getGlobalOptions() {
+ return globalOptions;
+ }
+
+ /**
+ * Gets the arguments
+ * @return Returns a String[]
+ */
+ protected String[] getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Gets the localOptions
+ * @return Returns a String[]
+ */
+ protected String[] getLocalOptions() {
+ return localOptions;
+ }
+
+ /**
+ * getRoot returns the folder the client was called with.
+ * (Sometimes that is not the folder you want to work with)
+ *
+ * @return Returns a ICVSResource
+ */
+ protected IManagedFolder getRoot() throws CVSException {
+
+ if (!mRoot.isFolder()) {
+ throw new CVSException(Policy.bind("Command.invalidRoot", new Object[] {mRoot.toString()}));
+ }
+
+ return mRoot;
+ }
+
+ /**
+ * Takes all the arguments and gives them back as resources from the
+ * root. This represents all the resources the client should work on.
+ *
+ * If there are no arguments gives the root folder back only.
+ */
+ protected IManagedResource[] getWorkResources() throws CVSException {
+ return getWorkResources(0);
+ }
+
+ /**
+ * Work like getWorkResources() but do not look at the first
+ * skip elements when creating the resources (this is useful when
+ * the first skip arguments of a command are not files but something
+ * else)
+ *
+ * @see Command#getWorkResources()
+ */
+ protected IManagedResource[] getWorkResources(int skip) throws CVSException {
+
+ IManagedResource[] result;
+
+ Assert.isTrue(arguments.length >= skip);
+
+ if (arguments.length == skip) {
+ return new IManagedResource[]{mRoot};
+ }
+
+ result = new IManagedResource[arguments.length - skip];
+
+ for (int i = skip; i<arguments.length; i++) {
+ result[i - skip] = mRoot.getChild(arguments[i]);
+ }
+
+ return result;
+ }
+
+ /**
+ * Get the resource that you are working with. This is a folder
+ * most of the time, but could be a file on some operations as
+ * well.
+ *
+ * It does also garantee that the WorkResource is a cvsFolder,
+ * or (if it is a file) does live in a cvsFolder.
+ *
+ * This does not apply to every operation (e.g. would not work on a
+ * checkout)
+ *
+ * @deprecated
+ */
+ protected IManagedResource getWorkResource(String relativeFolderPath) throws CVSException {
+
+ IManagedResource workResource;
+ IManagedFolder contextFolder;
+
+ workResource = getRoot().getChild(relativeFolderPath);
+
+ if (workResource.isFolder()) {
+ contextFolder = (IManagedFolder)workResource;
+ } else {
+ contextFolder = workResource.getParent();
+ }
+
+ if (!contextFolder.isCVSFolder()) {
+ throw new CVSException(Policy.bind("Command.invalidResource", new Object[] {contextFolder.toString()}));
+ }
+
+ return workResource;
+ }
+
+ /**
+ * If mResource is a folder:<br>
+ * Send all Directory under mResource as arguments to the server<br>
+ * If mResource is a file:<br>
+ * Send the file to the server<br>
+ * <br>
+ * Files that are changed are send with the content.
+ *
+ * @param modifiedOnly sends files that are modified only to the server
+ * @param emptyFolders sends the folder-entrie even if there is no file
+ to send in it
+ */
+ protected void sendFileStructure(IManagedResource mResource,
+ IProgressMonitor monitor,
+ boolean modifiedOnly,
+ boolean emptyFolders) throws CVSException {
+
+ FileStructureVisitor fsVisitor;
+
+ fsVisitor = new FileStructureVisitor(requestSender,mRoot,monitor,modifiedOnly,emptyFolders);
+
+ // FIXME: The accept should have an IProgressMonitor argment, not the above constructor
+ mResource.accept(fsVisitor);
+
+ }
+
+ /**
+ * Send an array of Resources.
+ *
+ * @see Command#sendFileStructure(IManagedResource,IProgressMonitor,boolean,boolean,boolean)
+ */
+ protected void sendFileStructure(IManagedResource[] mResources,
+ IProgressMonitor monitor,
+ boolean modifiedOnly,
+ boolean emptyFolders,
+ boolean newFolders) throws CVSException {
+
+ for (int i=0; i<mResources.length; i++) {
+ sendFileStructure(mResources[i],
+ monitor,
+ modifiedOnly,
+ emptyFolders);
+ }
+ }
+
+ /**
+ * Checks that all the workResources are managed Resources.
+ * (For folders we check isCVSFolder, because of a project-folder
+ * that is not managed, because it is not registerd in the
+ * parent-folder<br>
+ * To be used this way: Assert.isTrue(allArgumentsManaged())
+ *
+ * @throws AssertionFailedException if not all the arguments are
+ * managed
+ */
+ protected boolean allResourcesManaged() throws RuntimeException {
+
+ IManagedResource[] mWorkResources;
+
+ try {
+ mWorkResources = getWorkResources();
+
+ for (int i=0; i<mWorkResources.length; i++) {
+ if (mWorkResources[i].isFolder()) {
+ Assert.isTrue(((IManagedFolder) mWorkResources[i]).isCVSFolder());
+ } else {
+ Assert.isTrue(mWorkResources[i].isManaged());
+ }
+ }
+ } catch (CVSException e) {
+ Assert.isTrue(false);
+ }
+
+ return true;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/CommandDispatcher.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/CommandDispatcher.java
new file mode 100644
index 000000000..6f4e113f8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/CommandDispatcher.java
@@ -0,0 +1,109 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.Hashtable;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+/**
+ * Class that acctually runs the commands that come form the
+ * "command-line"
+ *
+ * @see CommandExecuter#execute(String command, IConnection, String[], String[], ICvsResource, OutputStream)
+ */
+public class CommandDispatcher {
+
+ private Hashtable commandPool;
+
+ private ResponseDispatcher responseDispatcher;
+ private RequestSender requestSender;
+
+ /**
+ * Puts all the Commands in a container in order to have access
+ * to them when they are going to be executed
+ *
+ * Generic approche to "plug in" new commands just by adding them
+ * to this constructor
+ */
+ public CommandDispatcher(ResponseDispatcher responseDispatcher,
+ RequestSender requestSender) {
+
+ commandPool = new Hashtable();
+
+ registerCommand(new Update(responseDispatcher,requestSender));
+ registerCommand(new Checkout(responseDispatcher,requestSender));
+ registerCommand(new Commit(responseDispatcher,requestSender));
+ registerCommand(new Import(responseDispatcher,requestSender));
+ registerCommand(new Add(responseDispatcher,requestSender));
+ registerCommand(new Remove(responseDispatcher,requestSender));
+ registerCommand(new Status(responseDispatcher,requestSender));
+ registerCommand(new Log(responseDispatcher,requestSender));
+ registerCommand(new Tag(responseDispatcher,requestSender));
+ registerCommand(new Admin(responseDispatcher,requestSender));
+ registerCommand(new Diff(responseDispatcher,requestSender));
+
+ }
+
+ /**
+ * Internal helper-method to put the commands into
+ * the hashtabe
+ */
+ private void registerCommand(ICommand command) {
+
+ // Do not register commands twice
+ Assert.isTrue(commandPool.get(command.getName()) == null);
+
+ commandPool.put(command.getName(),command);
+ }
+
+ /**
+ * Runs the given command on the cvs server.
+ *
+ * The only public method of the commands-package.
+ *
+ * Preconditiones:
+ * - all arguments non-null
+ * - globalOptions, localOptions arguments can be empty Arrays
+ * - the connection has to be set up
+ * - for most commands:
+ * root.isCVSFolder() = true ||
+ * root.getChild(arguments[0]) = true
+ *
+ * This method is not thread safe. In other words, this method is not to be
+ * invoked concurrently with the same connection or command name.
+ */
+ public void execute(String commandName,
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut) throws CVSException {
+
+ ICommand command;
+
+ Assert.isNotNull(commandPool.get(commandName));
+
+ command = (ICommand) commandPool.get(commandName);
+ command.execute(globalOptions,
+ localOptions,
+ arguments,
+ mRoot,
+ monitor,
+ messageOut);
+
+ }
+
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Commit.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Commit.java
new file mode 100644
index 000000000..2622d14d1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Commit.java
@@ -0,0 +1,61 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Commit extends Command {
+
+ public Commit(ResponseDispatcher responseDispathcer,
+ RequestSender requestSender) {
+
+ super(responseDispathcer,requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.CI;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.CI;
+ }
+
+ /**
+ * Send all files under the workingFolder as changed files to
+ * the server.
+ *
+ * @see Request#sendRequestsToServer(IProgressMonitor)
+ */
+ public void sendRequestsToServer(IProgressMonitor monitor) throws CVSException {
+
+ IManagedResource[] mWorkResources;
+
+ Assert.isTrue(allResourcesManaged());
+
+ // Get the folders we want to work on
+ mWorkResources = getWorkResources();
+
+ // Send all changed files to the server
+ sendFileStructure(mWorkResources,monitor,true,false,false);
+ sendHomeFolder();
+
+ }
+
+}
+
+ \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Diff.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Diff.java
new file mode 100644
index 000000000..ab226fdff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Diff.java
@@ -0,0 +1,65 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSDiffException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Diff extends AbstractMessageCommand {
+
+ /**
+ * Constructor for Diff.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Diff(ResponseDispatcher responseDispathcer, RequestSender requestSender) {
+ super(responseDispathcer, requestSender);
+ }
+
+ /**
+ * Overwritten to throw the CVSDiffException if the server returns
+ * an error, because it just does so when there is a difference between
+ * the cecked files.
+ */
+ public void execute (
+ String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException {
+
+ try {
+ super.execute(globalOptions,localOptions,arguments,mRoot,monitor,messageOut);
+ } catch (CVSServerException e) {
+ throw new CVSDiffException();
+ }
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.DIFF;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.DIFF;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileNameMatcher.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileNameMatcher.java
new file mode 100644
index 000000000..a01754b5c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileNameMatcher.java
@@ -0,0 +1,93 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.util.StringMatcher;
+
+/**
+ * A FileNameMatcher associates a String with a String pattern
+ * (e.g. a filename).
+ */
+public class FileNameMatcher {
+
+ private List matchers = new ArrayList();
+ private List results = new ArrayList();
+ private static final String TRUE = "true";
+ private static final String IGNORE_FILE = ".cvsignore";
+
+
+ public FileNameMatcher() {
+ }
+
+ public FileNameMatcher(String[] patterns) {
+ register(patterns);
+ }
+
+ void register(String[] patterns) {
+ for (int i = 0; i < patterns.length; i++) {
+ register(patterns[i],TRUE);
+ }
+ }
+
+ void register(String pattern, String result) {
+
+ Assert.isTrue(matchers.size() == results.size());
+
+ pattern = pattern.trim();
+
+ // The empty pattern matches everything, but we want to match
+ // nothing with it, so we just do not register anything
+ if (pattern.length() == 0) {
+ return;
+ }
+
+ matchers.add(new StringMatcher(pattern,false,false));
+ results.add(result);
+
+ }
+
+ public String getMatch(String name) {
+ StringMatcher stringMatcher;
+
+ for (int i = 0; i < matchers.size(); i++) {
+ stringMatcher = (StringMatcher) matchers.get(i);
+ if (stringMatcher.match(name)) {
+ return (String)results.get(i);
+ }
+ }
+
+ return null;
+ }
+
+ public boolean match(String name) {
+ return getMatch(name) != null;
+ }
+
+ /**
+ * Return a file name matcher build from the .cvsignore file
+ * in the provided directory or null if no such file exists
+ */
+ public static FileNameMatcher getIgnoreMatcherFor(ICVSFolder folder) throws CVSException, IOException {
+ ICVSFile cvsignore = folder.createFile(IGNORE_FILE);
+ if (!cvsignore.exists())
+ return null;
+ return new FileNameMatcher(cvsignore.getContent());
+
+ }
+
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileStructureVisitor.java
new file mode 100644
index 000000000..68485c1cf
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/FileStructureVisitor.java
@@ -0,0 +1,103 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * This visitor send the fileStructure to the requestSender.
+ *
+ * If accepted by an IManagedResource:<br>
+ * Send all Directory under mResource as arguments to the server<br>
+ * If accepted by a file:<br>
+ * Send the file to the server<br>
+ * <br>
+ * Files that are changed are send with the content.
+ *
+ * @param modifiedOnly sends files that are modified only to the server
+ * @param emptyFolders sends the folder-entrie even if there is no file
+ to send in it
+ */
+
+class FileStructureVisitor extends AbstractStructureVisitor {
+
+ private final boolean modifiedOnly;
+ private final boolean emptyFolders;
+
+ /**
+ * Constructor for the visitor
+ *
+ * @param modifiedOnly sends files that are modified only to the server
+ * @param emptyFolders sends the folder-entrie even if there is no file
+ to send in it
+ */
+ public FileStructureVisitor(RequestSender requestSender,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ boolean modifiedOnly,
+ boolean emptyFolders) {
+
+ super(requestSender, mRoot, monitor);
+ this.modifiedOnly = modifiedOnly;
+ this.emptyFolders = emptyFolders;
+
+ }
+
+ /**
+ * @see IManagedVisitor#visitFile(IManagedFile)
+ */
+ public void visitFile(IManagedFile mFile) throws CVSException {
+
+ // We assume, that acceptChildren() does call all the files
+ // and then the folder or first all the folders and then the
+ // files and does not mix. This is specified as well.
+
+ if (!modifiedOnly || mFile.isDirty()) {
+ // sendFile sends the folder if it is nessary
+ sendFile(mFile);
+ }
+ }
+
+ /**
+ * @see IManagedVisitor#visitFolder(IManagedFolder)
+ */
+ public void visitFolder(IManagedFolder mFolder) throws CVSException {
+
+ if (emptyFolders) {
+ // If we want to send empty folder, that just send it when
+ // we come to it
+ sendFolder(mFolder);
+ }
+
+ if (mFolder.exists()) {
+ mFolder.acceptChildren(this);
+ }
+
+ }
+
+ private void sendFile(IManagedFile mFile) throws CVSException {
+
+ // Send the folder if it hasn't been send so far
+ sendFolder(mFile.getParent());
+
+ if (mFile.getFileInfo() == null) {
+ sendFile(mFile,true,null);
+ } else {
+ sendFile(mFile,true,mFile.getFileInfo().getKeywordMode());
+ }
+ }
+
+ private void sendFolder(IManagedFolder mFolder) throws CVSException{
+ sendFolder(mFolder,false);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ICommand.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ICommand.java
new file mode 100644
index 000000000..4280c1031
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ICommand.java
@@ -0,0 +1,56 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * Represents a command of the cvs-client.
+ *
+ * It gets the information provided on the command-line
+ * and has to communicate to the server.
+ *
+ * Normaly the response of the server is handeld by GeneralResponseHandler.
+ *
+ * If custom-response-handling is needed the class should register a custom
+ * handler at the commandExecuter.
+ */
+
+interface ICommand {
+
+ /**
+ * Runs the command.
+ *
+ * @param global Options is allowed to have null-elements for convinience (all the others are not)
+ * @see CommandExecuter#execute(String command, IConnection, String[], String[], ICvsResource, OutputStream)
+ */
+ void execute(String[] globalOptions,
+ String[] localOptions,
+ String[] arguments,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ PrintStream messageOut)
+ throws CVSException;
+
+ /**
+ * Returns the responses type. This is the name of
+ * the CVS command in String-Form (lowcase, like the
+ * command in the cvs-client)
+ */
+ public String getName();
+
+ /**
+ * Returns the name of the request that is send in order to
+ * start this command.
+ * This can be different from the name.
+ * e.g. the cvs-command "commit" sends "ci" to the server.
+ */
+ public String getRequestName();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Import.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Import.java
new file mode 100644
index 000000000..34751a018
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Import.java
@@ -0,0 +1,83 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+class Import extends Command {
+
+ /**
+ * Constructor for Import.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Import(ResponseDispatcher responseDispatcher,
+ RequestSender requestSender) {
+ super(responseDispatcher, requestSender);
+ }
+
+ /**
+ * @see Command#sendRequestsToServer(IProgressMonitor)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor)
+ throws CVSException {
+
+ String mode = null;
+ String[] wrappers;
+ String[] ignores;
+ IManagedVisitor visitor;
+
+ // If the arguments are not three, the server is going to
+ // reject the request
+ // NOTE: Yes, but at least the user would get better feedback!
+ // We should be throwing a CVSException!
+ Assert.isTrue(getArguments().length == 3);
+
+ // At this point we need to know wether we need to send the file
+ // as a binary. The server will set the mode properly based on the wrapper option.
+ if (Util.isOption(getLocalOptions(),FileProperties.BINARY_TAG)) {
+ mode = FileProperties.BINARY_TAG;
+ }
+
+ ignores = Util.getOptions(getLocalOptions(),Client.IGNORE_OPTION,false);
+ wrappers = Util.getOptions(getLocalOptions(),Client.WRAPPER_OPTION,false);
+
+ visitor = new ImportStructureVisitor(requestSender,
+ getRoot(),
+ monitor,
+ mode,
+ ignores,
+ wrappers);
+
+ getRoot().accept(visitor);
+
+ sendHomeFolder(false);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.IMPORT;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.IMPORT;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ImportStructureVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ImportStructureVisitor.java
new file mode 100644
index 000000000..0cca03da2
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/ImportStructureVisitor.java
@@ -0,0 +1,140 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.util.StringMatcher;
+
+/**
+ * The ImportStructureVisitor sends the content of the folder it is
+ * used on to the server. It constructs the locations of the resources
+ * because the resources do not yet have a remote-location.<br>
+ * Up to that it can ignore certain files and decides wether to send
+ * a file in binary or text mode due to a specification that is passed
+ * as a "wrapper" argument.
+ */
+class ImportStructureVisitor extends AbstractStructureVisitor {
+
+ private static final String KEYWORD_OPTION = "-k";
+ private static final String QUOTE = "'";
+
+ private final String mode;
+ private final String[] ignores;
+ private final String[] wrappers;
+
+ private final FileNameMatcher ignoreMatcher;
+ private final FileNameMatcher wrapMatcher;
+
+ /**
+ * Constructor for ImportStructureVisitor.
+ * @param requestSender
+ * @param mRoot
+ * @param monitor
+ */
+ public ImportStructureVisitor(
+ RequestSender requestSender,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor,
+ String mode,
+ String[] ignores,
+ String[] wrappers) {
+ super(requestSender, mRoot, monitor);
+
+ this.mode = mode;
+ this.ignores = ignores;
+ ignoreMatcher = new FileNameMatcher(ignores);
+
+ this.wrappers = wrappers;
+ wrapMatcher = initWrapMatcher(wrappers);
+ }
+
+
+ /**
+ * Inits the wrapMatcher, that is responsible to find out
+ * whether a file is to be send as a binary (on an import)
+ * or not.
+ *
+ * Takes wrappers of this format:
+ * *.class -k 'o'
+ *
+ * and inits the FileNameMatcher to give
+ * -ko back if you call it with match("somename.class")
+ *
+ * ignores all wrappers, that do not contain -k
+ */
+ private FileNameMatcher initWrapMatcher(String[] wrappers) {
+
+ FileNameMatcher wrapMatcher;
+
+ if (wrappers == null) {
+ return null;
+ }
+
+ wrapMatcher = new FileNameMatcher();
+
+ for (int i = 0; i < wrappers.length; i++) {
+
+ if (wrappers[i].indexOf(KEYWORD_OPTION) == -1) {
+ continue;
+ }
+
+ StringTokenizer st = new StringTokenizer(wrappers[i]);
+ String pattern = st.nextToken();
+ String option = st.nextToken();
+ // get rid of the quotes
+ StringTokenizer quoteSt =
+ new StringTokenizer(st.nextToken(),QUOTE);
+ option += quoteSt.nextToken();
+
+ wrapMatcher.register(pattern,option);
+ }
+
+ return wrapMatcher;
+ }
+
+ /**
+ * @see IManagedVisitor#visitFile(IManagedFile)
+ */
+ public void visitFile(IManagedFile mFile) throws CVSException {
+
+ String mode = this.mode;
+
+ if (ignoreMatcher != null && ignoreMatcher.match(mFile.getName())) {
+ return;
+ }
+
+ if (mode == null && wrapMatcher != null) {
+ mode = wrapMatcher.getMatch(mFile.getName());
+ }
+
+ sendFile(mFile,false,mode);
+
+ }
+
+ /**
+ * @see IManagedVisitor#visitFolder(IManagedFolder)
+ */
+ public void visitFolder(IManagedFolder mFolder) throws CVSException {
+
+ if (ignoreMatcher != null && ignoreMatcher.match(mFolder.getName())) {
+ return;
+ }
+
+ sendFolder(mFolder,true);
+ mFolder.acceptChildren(this);
+
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Log.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Log.java
new file mode 100644
index 000000000..40c85eaa9
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Log.java
@@ -0,0 +1,37 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Log extends AbstractMessageCommand {
+
+ /**
+ * Constructor for Log.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Log(ResponseDispatcher responseContainer, RequestSender requestSender) {
+ super(responseContainer, requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.LOG;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.LOG;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/PruneFolderVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/PruneFolderVisitor.java
new file mode 100644
index 000000000..44c363987
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/PruneFolderVisitor.java
@@ -0,0 +1,39 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+
+/**
+ * Goes recursivly through the folders checks if they are empyty
+ * and deletes them. Of course it is starting at the leaves of the
+ * recusion (the folders that do not have subfolders).
+ */
+public class PruneFolderVisitor implements IManagedVisitor {
+
+ /**
+ * @see IManagedVisitor#visitFile(IManagedFile)
+ */
+ public void visitFile(IManagedFile file) throws CVSException {
+ }
+
+ /**
+ * @see IManagedVisitor#visitFolder(IManagedFolder)
+ */
+ public void visitFolder(IManagedFolder folder) throws CVSException {
+ folder.acceptChildren(this);
+ if (folder.getFiles().length == 0 &&
+ folder.getFolders().length == 0) {
+ folder.setFolderInfo(null);
+ folder.delete();
+ }
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Remove.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Remove.java
new file mode 100644
index 000000000..08fbe0a5d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Remove.java
@@ -0,0 +1,60 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+
+class Remove extends Command {
+
+ /**
+ * Constructor for Remove.
+ * @param responseContainer
+ * @param requestSender
+ */
+ public Remove(
+ ResponseDispatcher responseContainer,
+ RequestSender requestSender) {
+ super(responseContainer, requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.REMOVE;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.REMOVE;
+ }
+
+ /**
+ * @see Command#sendRequestsToServer(IProgressMonitor)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor)
+ throws CVSException {
+
+ IManagedResource[] mWorkResources;
+
+ Assert.isTrue(allResourcesManaged());
+
+ // Get the folders we want to work on
+ mWorkResources = getWorkResources();
+
+ // Send all changed files to the server
+ sendFileStructure(mWorkResources,monitor,true,false,false);
+ sendHomeFolder();
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Status.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Status.java
new file mode 100644
index 000000000..8335648ce
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Status.java
@@ -0,0 +1,38 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+
+public class Status extends AbstractMessageCommand {
+
+ public Status(ResponseDispatcher responseContainer,
+ RequestSender requestSender) {
+
+ super(responseContainer,requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.STATUS;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.STATUS;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Tag.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Tag.java
new file mode 100644
index 000000000..e0a23bfa4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Tag.java
@@ -0,0 +1,58 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+public class Tag extends Command {
+
+ /**
+ * Constructor for Tag.
+ * @param responseDispatcher
+ * @param requestSender
+ */
+ public Tag(ResponseDispatcher responseDispatcher, RequestSender requestSender) {
+ super(responseDispatcher, requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.TAG;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.TAG;
+ }
+
+ /**
+ * @see Command#sendRequestsToServer(IProgressMonitor)
+ */
+ protected void sendRequestsToServer(IProgressMonitor monitor)
+ throws CVSException {
+
+ // Either we got parameters or the folder we are in is an cvsFolder
+ Assert.isTrue(getArguments().length > 1 ||
+ getRoot().isCVSFolder());
+
+ // Get the folders we want to work on, ignoring the first argument
+ IManagedResource[] mWorkResources = getWorkResources(1);
+
+ // Send all folders that are already managed to the server
+ sendFileStructure(mWorkResources,monitor,false,false,false);
+ sendHomeFolder();
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Update.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Update.java
new file mode 100644
index 000000000..6a319c606
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/commands/Update.java
@@ -0,0 +1,90 @@
+package org.eclipse.team.internal.ccvs.core.commands;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.response.ResponseDispatcher;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+class Update extends Command {
+
+ public Update(ResponseDispatcher responseDispatcher,
+ RequestSender requestSender) {
+
+ super(responseDispatcher,requestSender);
+ }
+
+ /**
+ * @see ICommand#getName()
+ */
+ public String getName() {
+ return RequestSender.UPDATE;
+ }
+
+ /**
+ * @see ICommand#getRequestName()
+ */
+ public String getRequestName() {
+ return RequestSender.UPDATE;
+ }
+
+ /**
+ * MV: Special case handling for the "." argument.
+ *
+ */
+// protected IManagedResource[] getWorkResources() throws CVSException {
+// // NIK: Do we need this handling ?
+// // MV: You tell me!
+// if ((getArguments().length == 1) && (getArguments()[0].equals(".")))
+// return new IManagedResource[]{getRoot()};
+// return super.getWorkResources();
+// }
+
+ public void sendRequestsToServer(IProgressMonitor monitor) throws CVSException {
+
+ IManagedResource[] mWorkResources;
+
+ Assert.isTrue(allResourcesManaged());
+
+ // Get the folders we want to work on
+ mWorkResources = getWorkResources();
+
+ // FIXME other clients send this (wondering if we should as well):
+ // requestSender.writeLine("UseUnchanged");
+ // requestSender.writeLine("Case");
+ // requestSender.sendArgument("-u");
+
+ // Send all folders that are already managed to the server
+ // even folders that are empty
+ sendFileStructure(mWorkResources,monitor,false,true,false);
+ sendHomeFolder();
+
+ }
+
+ /**
+ * On sucessful finish, prune empty directories if
+ * the -P option was specified.
+ */
+ protected void finished(boolean success) throws CVSException {
+ if (success && Util.isOption(getLocalOptions(), Client.PRUNE_OPTION)) {
+ // Get the folders we want to work on
+ IManagedResource[] mWorkResources = getWorkResources();
+ // Delete empty directories
+ IManagedVisitor visitor = new PruneFolderVisitor();
+ for (int i=0; i<mWorkResources.length; i++) {
+ mWorkResources[i].accept(visitor);
+ }
+ }
+ }
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSAuthenticationException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSAuthenticationException.java
new file mode 100644
index 000000000..683aa1f9a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSAuthenticationException.java
@@ -0,0 +1,55 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+public class CVSAuthenticationException extends CVSException {
+
+ /**
+ * Creates a new <code>CVSAuthenticationException</code>
+ *
+ * @param detail a message that describes the exception in detail.
+ */
+ public CVSAuthenticationException(String detail) {
+ super(
+ Policy.bind("CVSAuthenticationException.detail", new Object[] { detail }),
+ null,
+ null);
+ }
+ /**
+ * Creates a new <code>CVSAuthenticationException</code>
+ *
+ * @param cvsroot the cvs server.
+ * @param detail a message that describes the exception in detail.
+ */
+ public CVSAuthenticationException(String cvsroot, String detail) {
+ this(detail);
+ }
+ /**
+ * Creates a new <code>CVSAuthenticationException</code>
+ *
+ * @param status the status result describing this exception.
+ */
+ public CVSAuthenticationException(IStatus status) {
+ super(status);
+ }
+ /**
+ * Creates a new <code>CVSAuthenticationException</code>
+ *
+ * @param cvsroot the cvs server.
+ * @param throwable the exception that has caused the authentication
+ * failure.
+ */
+ public CVSAuthenticationException(String cvsroot, Throwable throwable) {
+ super(
+ Policy.bind("CVSAuthenticationException.normal", new Object[] { cvsroot }),
+ null,
+ null);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSCommunicationException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSCommunicationException.java
new file mode 100644
index 000000000..8fb6f7756
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSCommunicationException.java
@@ -0,0 +1,46 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+public class CVSCommunicationException extends CVSException {
+
+ /**
+ * Create a new <code>CVSCommunicationException with the
+ * given message.
+ */
+ public CVSCommunicationException(String message) {
+ super(message, null, null);
+ }
+ /**
+ * Create a new <code>CVSCommunicationException.
+ *
+ * @param message a message describing the exception in detail.
+ * @param the caught exception that has caused the communication
+ * exception.
+ */
+ public CVSCommunicationException(String message, Throwable throwable) {
+ super(message, null, throwable);
+ }
+ /**
+ * Create a new <code>CVSCommunicationException.
+ *
+ * @param the caught exception that has caused the communication
+ * exception.
+ */
+ public CVSCommunicationException(Throwable throwable) {
+ this(getMessageFor(throwable), throwable);
+ }
+
+ public static String getMessageFor(Throwable throwable) {
+ String message = Policy.bind(throwable.getClass().getName(), new Object[] {throwable.getMessage()});
+ if (message.equals(throwable.getClass().getName()))
+ message = Policy.bind("CVSCommunicationException.io", new Object[] {throwable.toString()});
+ return message;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSFileException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSFileException.java
new file mode 100644
index 000000000..9a5c1e770
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSFileException.java
@@ -0,0 +1,35 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+// NIK: this class is nerver used (once in a catch statment)
+
+public class CVSFileException extends CVSException {
+
+ /**
+ * Creates a new <code>CVSFileException</code>.
+ *
+ * @param message a message describing the exception in detail.
+ * @param path the file's path that has caused the exception.
+ */
+ public CVSFileException(String message, IPath path) {
+ super(message, path, null);
+ }
+ /**
+ * Creates a new <code>CVSFileException</code>.
+ *
+ * @param path the file's path that has caused the exception.
+ * @param throwable the caught exception that has caused the communication
+ * exception.
+ */
+ public CVSFileException(IPath path, Throwable throwable) {
+ super(Policy.bind("CVSFileException.io"), path, throwable);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSRepositoryLocation.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSRepositoryLocation.java
new file mode 100644
index 000000000..a731a5563
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSRepositoryLocation.java
@@ -0,0 +1,570 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IConnectionMethod;
+import org.eclipse.team.ccvs.core.IUserAuthenticator;
+import org.eclipse.team.ccvs.core.IUserInfo;
+
+/**
+ * This class manages a CVS repository location.
+ *
+ * It provides the mapping between connection method name and the
+ * plugged in ICunnectionMethod.
+ *
+ * It parses location strings into instances.
+ *
+ * It provides a method to open a connection to the server along
+ * with a method to validate that connections can be made.
+ *
+ * It manages its user info using the plugged in IUserAuthenticator
+ * (unless a username and password are provided as part of the creation
+ * string, in which case, no authenticator is used).
+ *
+ * Instances must be disposed of when no longer needed in order to
+ * notify the authenticator so cached properties can be cleared
+ *
+ */
+public class CVSRepositoryLocation implements ICVSRepositoryLocation, IUserInfo {
+
+ // static variables for extension points
+ private static IUserAuthenticator authenticator;
+ private static IConnectionMethod[] pluggedInConnectionMethods = null;
+
+ private IConnectionMethod method;
+ private String user;
+ private String password;
+ private String host;
+ private int port;
+ private String root;
+ private boolean userFixed;
+ private boolean passwordFixed;
+
+ public static final char COLON = ':';
+ public static final char HOST_SEPARATOR = '@';
+ public static final char PORT_SEPARATOR = '#';
+ public static final boolean STANDALONE_MODE = (System.getProperty("cvs.standalone")==null)?false:(new Boolean(System.getProperty("cvs.standalone")).booleanValue());
+
+ /**
+ * Create a CVSRepositoryLocation from its composite parts.
+ */
+ private CVSRepositoryLocation(IConnectionMethod method, String user, String password, String host, int port, String root, boolean userFixed, boolean passwordFixed) {
+ this.method = method;
+ this.user = user;
+ this.password = password;
+ this.host = host;
+ this.port = port;
+ this.root = root;
+ // The username can be fixed only if one is provided
+ if (userFixed && (user != null))
+ this.userFixed = true;
+ // The password can only be fixed if the username is and a password is provided
+ if (userFixed && passwordFixed && (password != null))
+ this.passwordFixed = true;
+// else {
+// // If the password is not fixed, there's no need to fix the username
+// this.userFixed = false;
+// this.passwordFixed = false;
+// }
+ // Retrieve a password if one was previosuly cached or set it to blank
+ if (!passwordFixed) {
+ IUserAuthenticator authenticator = getAuthenticator();
+ if (authenticator != null) {
+ try {
+ if (!authenticator.retrievePassword(this, this))
+ password = "";
+ } catch (CVSException e) {
+ password = "";
+ }
+ }
+ }
+ }
+
+ /**
+ * Create the connection to the remote server.
+ * If anything fails, an exception will be thrown and must
+ * be handled by the caller.
+ */
+ private Connection createConnection() throws CVSException {
+ // Should the open() of Connection be done in the constructor?
+ // The only reason it should is if connections can be reused (they aren't reused now).
+ Connection connection = new Connection(this, method.createConnection(this, password));
+ connection.open();
+ return connection;
+ }
+
+ /**
+ * Dispose of the receiver by clearing any cached authorization information.
+ * This method shold only be invoked when the corresponding adapter is shut
+ * down or a connection is being validated.
+ */
+ protected void dispose() throws CVSException {
+ IUserAuthenticator authenticator = getAuthenticator();
+ if (authenticator != null) {
+ authenticator.dispose(this);
+ }
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getHost()
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @see IRepositoryLocation#getLocation()
+ *
+ * The username is inlcuded if it is fixed.
+ * The password is never included even if it is fixed.
+ * The port is included if it is not the default port.
+ */
+ public String getLocation() {
+ return COLON + method.getName() + COLON +
+ (userFixed?(user +
+ (passwordFixed?(COLON + password):"")
+ + HOST_SEPARATOR):"") +
+ host +
+ ((port == USE_DEFAULT_PORT)?"":(PORT_SEPARATOR + new Integer(port).toString())) +
+ COLON + root;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getMethod()
+ */
+ public IConnectionMethod getMethod() {
+ return method;
+ }
+
+ public boolean setMethod(String methodName) {
+ IConnectionMethod newMethod = getPluggedInConnectionMethod(methodName);
+ if (newMethod == null)
+ return false;
+ method = newMethod;
+ return true;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getPort()
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getRootDirectory()
+ */
+ public String getRootDirectory() {
+ return root;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getTimeout()
+ *
+ * For the time being, the timeout value is a system wide value
+ * associated with the CVSPlugin singleton.
+ */
+ public int getTimeout() {
+ return 60;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getUserInfo()
+ */
+ public IUserInfo getUserInfo() {
+ return this;
+ }
+
+ /**
+ * @see ICVSRepositoryLocation#getUsername()
+ * @see IUserInfo#getUsername()
+ */
+ public String getUsername() {
+ return user;
+ }
+
+ /**
+ * @see IUserInfo#isUsernameMutable()
+ */
+ public boolean isUsernameMutable() {
+ return !userFixed;
+ }
+
+ /**
+ * Open a connection to the repository represented by the receiver.
+ * If the username or password are not fixed, openConnection will
+ * use the plugged-in authenticator to prompt for the username and/or
+ * password if one has not previously been provided or if the previously
+ * supplied username and password are invalid.
+ */
+ public Connection openConnection() throws CVSException {
+ String message = null;
+
+ // If we have a username and password, don't authenticate unless we fail.
+ // We would have a username and password if we previously authenticated
+ // or one was stored using storePassword()
+ if ((user != null) && (password != null))
+ try {
+ return createConnection();
+ } catch (CVSAuthenticationException ex) {
+ if (userFixed && passwordFixed)
+ throw ex;
+ message = ex.getMessage();
+ }
+
+ // If we failed above or we didn't have a username or password, authenticate
+ IUserAuthenticator authenticator = getAuthenticator();
+ if (authenticator == null) {
+ throw new CVSAuthenticationException(this.getLocation(), Policy.bind("Client.noAuthenticator"));
+ }
+
+ // If we tried above and failed, this is a retry.
+ boolean retry = (message != null);
+ while (true) {
+ try {
+ if (!authenticator.authenticateUser(this, this, retry, message))
+ throw new CVSAuthenticationException(new CVSStatus(CVSStatus.ERROR, Policy.bind("error")));
+ } catch (CVSException e) {
+ throw e;
+ }
+ try {
+ // The following will throw an exception if authentication fails
+ return createConnection();
+ } catch (CVSAuthenticationException ex) {
+ retry = true;
+ message = ex.getMessage();
+ }
+ }
+ }
+
+ /**
+ * Implementation of inherited toString()
+ */
+ public String toString() {
+ return getLocation();
+ }
+
+ /**
+ * @see IUserInfo#setPassword(String)
+ */
+ public void setPassword(String password) {
+ if (passwordFixed)
+ throw new UnsupportedOperationException();
+ this.password = password;
+ }
+
+ /**
+ * @see IUserInfo#setUsername(String)
+ */
+ public void setUsername(String user) {
+ if (userFixed)
+ throw new UnsupportedOperationException();
+ this.user = user;
+ }
+
+ public void setUserMuteable(boolean muteable) {
+ userFixed = !muteable;
+ }
+
+ public void storePassword(String password) throws CVSException {
+ IUserAuthenticator authenticator = getAuthenticator();
+ if (authenticator != null) {
+ authenticator.cachePassword(this, this, password);
+ }
+ }
+
+ public void updateCache() throws CVSException {
+ IUserAuthenticator authenticator = getAuthenticator();
+ if (authenticator != null) {
+ authenticator.cachePassword(this, this, password);
+ }
+ }
+ /**
+ * Validate that the receiver contains valid information for
+ * making a connection. If the receiver contains valid
+ * information, the method returns. Otherwise, an exception
+ * indicating the problem is throw.
+ */
+ public boolean validateConnection() throws CVSException {
+ try {
+ openConnection().close();
+ return true;
+ } catch (CVSException e) {
+ // If the validation failed, dispose of any cached info
+ dispose();
+ throw e;
+ }
+ }
+
+ public static boolean validateConnectionMethod(String methodName) {
+ String[] methods = CVSTeamProvider.getConnectionMethods();
+ for (int i=0;i<methods.length;i++) {
+ if (methodName.equals(methods[i]))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Parse a location string and return a CVSRepositoryLocation.
+ *
+ * On failure, the status of the exception will be a MultiStatus
+ * that includes the original parsing error and a general status
+ * displaying the passed location and proper form. This form is
+ * better for logging, etc.
+ */
+ public static CVSRepositoryLocation fromString(String location) throws CVSException {
+ try {
+ return fromString(location, false);
+ } catch (CVSException e) {
+ // Parsing failed. Include a status that
+ // shows the passed location and the proper form
+ MultiStatus error = new MultiStatus(CVSProviderPlugin.ID, CVSStatus.ERROR, Policy.bind("CVSRepositoryLocation.invalidFormat", new Object[] {location}), null);
+ error.merge(new CVSStatus(IStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.locationForm")));
+ error.merge(e.getStatus());
+ throw new CVSException(error);
+ }
+ }
+
+ /**
+ * Parse a location string and return a CVSRepositoryLocation.
+ *
+ * The valid format (from the cederqvist) is:
+ *
+ * :method:[[user][:password]@]hostname[:[port]]/path/to/repository
+ *
+ * However, this does not work with CVS on NT so we use the format
+ *
+ * :method:[user[:password]@]hostname[#port]:/path/to/repository
+ *
+ * Some differences to note:
+ * The : after the host/port is not optional because of NT naming including device
+ * e.g. :pserver:username:password@hostname#port:D:\cvsroot
+ *
+ * If validateOnly is true, this method will always throw an exception.
+ * The status of the exception indicates success or failure. The status
+ * of the exception contains a specific message suitable for displaying
+ * to a user who has knowledge of the provided location string.
+ * @see CVSRepositoryLocation.fromString(String)
+ */
+ public static CVSRepositoryLocation fromString(String location, boolean validateOnly) throws CVSException {
+ String partId = null;
+ try {
+ // Get the connection method
+ partId = "CVSRepositoryLocation.parsingMethod";
+ int start = location.indexOf(COLON);
+ if (start != 0)
+ throw new CVSException(Policy.bind("CVSRepositoryLocation.startOfLocation"));
+ int end = location.indexOf(COLON, start + 1);
+ String methodName = location.substring(start + 1, end);
+ IConnectionMethod method = getPluggedInConnectionMethod(methodName);
+ if (method == null)
+ throw new CVSException(new CVSStatus(CVSStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.methods", new Object[] {getPluggedInConnectionMethodNames()})));
+
+ // Get the user name and password (if provided)
+ partId = "CVSRepositoryLocation.parsingUser";
+ start = end + 1;
+ end = location.indexOf(HOST_SEPARATOR, start);
+ String user = null;;
+ String password = null;
+ // if end is -1 then there is no host separator meaning that the username is not present
+ if (end != -1) {
+ // Get the optional user and password
+ user = location.substring(start, end);
+ // Separate the user and password (if there is a password)
+ start = user.indexOf(COLON);
+ if (start != -1) {
+ partId = "CVSRepositoryLocation.parsingPassword";
+ password = user.substring(start+1);
+ user = user.substring(0, start);
+ }
+ // Set start to point after the host separator
+ start = end + 1;
+ }
+
+ // Get the host (and port)
+ partId = "CVSRepositoryLocation.parsingHost";
+ end= location.indexOf(COLON, start);
+ String host = location.substring(start, end);
+ int port = USE_DEFAULT_PORT;
+ // Separate the port and host if there is a port
+ start = host.indexOf(PORT_SEPARATOR);
+ if (start != -1) {
+ partId = "CVSRepositoryLocation.parsingPort";
+ port = Integer.parseInt(host.substring(start+1));
+ host = host.substring(0, start);
+ }
+
+ // Get the repository path (translating backslashes to slashes)
+ partId = "CVSRepositoryLocation.parsingRoot";
+ start = end + 1;
+ String root = location.substring(start).replace('\\', '/');
+
+ if (validateOnly)
+ throw new CVSException(new CVSStatus(CVSStatus.OK, Policy.bind("ok")));
+
+ return new CVSRepositoryLocation(method, user, password, host, port, root, (user != null), (password != null));
+ }
+ catch (IndexOutOfBoundsException e) {
+ // We'll get here if anything funny happened while extracting substrings
+ throw new CVSException(Policy.bind(partId));
+ }
+ catch (NumberFormatException e) {
+ // We'll get here if we couldn't parse a number
+ throw new CVSException(Policy.bind(partId));
+ }
+ }
+
+ public static IUserAuthenticator getAuthenticator() {
+ if (authenticator == null) {
+ authenticator = getPluggedInAuthenticator();
+ }
+ return authenticator;
+ }
+
+ /**
+ * Return the connection method registered for the given name or null if none
+ * are registered
+ */
+ private static IConnectionMethod getPluggedInConnectionMethod(String methodName) {
+ IConnectionMethod[] methods = getPluggedInConnectionMethods();
+ for(int i=0; i<methods.length; i++) {
+ if(methodName.equals(methods[i].getName()))
+ return methods[i];
+ }
+ return null;
+ }
+
+ /**
+ * Return a string containing a list of all connection methods
+ */
+ private static String getPluggedInConnectionMethodNames() {
+ IConnectionMethod[] methods = getPluggedInConnectionMethods();
+ StringBuffer methodNames = new StringBuffer();
+ for(int i=0; i<methods.length; i++) {
+ String name = methods[i].getName();
+ if (i>0)
+ methodNames.append(", ");
+ methodNames.append(name);
+ }
+ return methodNames.toString();
+ }
+
+ public static IConnectionMethod[] getPluggedInConnectionMethods() {
+ if(pluggedInConnectionMethods==null) {
+ List connectionMethods = new ArrayList();
+
+ if (STANDALONE_MODE) {
+ connectionMethods.add(new PServerConnectionMethod());
+ } else {
+ IExtension[] extensions = Platform.getPluginRegistry().getExtensionPoint(CVSProviderPlugin.ID, CVSProviderPlugin.PT_CONNECTIONMETHODS).getExtensions();
+ for(int i=0; i<extensions.length; i++) {
+ IExtension extension = extensions[i];
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length == 0) {
+ CVSProviderPlugin.log(new Status(IStatus.ERROR, CVSProviderPlugin.ID, 0, Policy.bind("CVSProviderPlugin.execProblem"), null));
+ continue;
+ }
+ try {
+ IConfigurationElement config = configs[0];
+ connectionMethods.add(config.createExecutableExtension("run"));
+ } catch (CoreException ex) {
+ CVSProviderPlugin.log(new Status(IStatus.ERROR, CVSProviderPlugin.ID, 0, Policy.bind("CVSProviderPlugin.execProblem"), ex));
+ }
+ }
+ }
+ pluggedInConnectionMethods = (IConnectionMethod[])connectionMethods.toArray(new IConnectionMethod[0]);
+ }
+ return pluggedInConnectionMethods;
+ }
+
+ private static IUserAuthenticator getPluggedInAuthenticator() {
+ IExtension[] extensions = Platform.getPluginRegistry().getExtensionPoint(CVSProviderPlugin.ID, CVSProviderPlugin.PT_AUTHENTICATOR).getExtensions();
+ if (extensions.length == 0)
+ return null;
+ IExtension extension = extensions[0];
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length == 0) {
+ CVSProviderPlugin.log(new Status(IStatus.ERROR, CVSProviderPlugin.ID, 0, Policy.bind("CVSAdapter.noConfigurationElement", new Object[] {extension.getUniqueIdentifier()}), null));
+ return null;
+ }
+ try {
+ IConfigurationElement config = configs[0];
+ return (IUserAuthenticator) config.createExecutableExtension("run");
+ } catch (CoreException ex) {
+ CVSProviderPlugin.log(new Status(IStatus.ERROR, CVSProviderPlugin.ID, 0, Policy.bind("CVSAdapter.unableToInstantiate", new Object[] {extension.getUniqueIdentifier()}), ex));
+ return null;
+ }
+ }
+ /**
+ * Validate that the given string could ne used to succesfully create
+ * an instance of the receiver.
+ *
+ * This method performs some initial checks to provide displayable
+ * feedback and also tries a more in-depth parse using fromString(String, boolean).
+ */
+ public static IStatus validate(String location) {
+
+ // Check some simple things that are not checked in creation
+ if (location == null)
+ return new CVSStatus(CVSStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.nullLocation"));
+ if (location.equals(""))
+ return new CVSStatus(CVSStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.emptyLocation"));
+ if (location.endsWith(" ") || location.endsWith("\t"))
+ return new CVSStatus(CVSStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.endWhitespace"));
+ if (!location.startsWith(":") || location.indexOf(COLON, 1) == -1)
+ return new CVSStatus(CVSStatus.ERROR, null, Policy.bind("CVSRepositoryLocation.startOfLocation"));
+
+ // Do some quick checks to provide geberal feedback
+ String formatError = Policy.bind("CVSRepositoryLocation.locationForm");
+ int secondColon = location.indexOf(COLON, 1);
+ int at = location.indexOf(HOST_SEPARATOR);
+ if (at != -1) {
+ String user = location.substring(secondColon + 1, at);
+ if (user.equals(""))
+ return new CVSStatus(CVSStatus.ERROR, null, formatError);
+ } else
+ at = secondColon;
+ int colon = location.indexOf(COLON, at + 1);
+ if (colon == -1)
+ return new CVSStatus(CVSStatus.ERROR, null, formatError);
+ String host = location.substring(at + 1, colon);
+ if (host.equals(""))
+ return new CVSStatus(CVSStatus.ERROR, null, formatError);
+ String path = location.substring(colon + 1, location.length());
+ if (path.equals(""))
+ return new CVSStatus(CVSStatus.ERROR, null, formatError);
+
+ // Do a full parse and see if it passes
+ try {
+ fromString(location, true);
+ } catch (CVSException e) {
+ // An exception is always throw. Return the status
+ return e.getStatus();
+ }
+
+ // Looks ok (we'll actually never get here because above
+ // fromString(String, boolean) will always throw an exception).
+ return new CVSStatus(IStatus.OK, Policy.bind("ok"));
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSServerException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSServerException.java
new file mode 100644
index 000000000..64ec8a80b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/CVSServerException.java
@@ -0,0 +1,21 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * Client has received an error response from the server.
+ */
+public class CVSServerException extends CVSException {
+ public CVSServerException(String message) {
+ super(new Status(IStatus.ERROR, CVSProviderPlugin.ID, UNABLE, message, null));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/Connection.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/Connection.java
new file mode 100644
index 000000000..e88f10a45
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/Connection.java
@@ -0,0 +1,339 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.ccvs.core.*;
+import org.eclipse.team.ccvs.core.*;
+
+/**
+ * A connection to talk to a cvs server. The life cycle of a connection is
+ * as follows:
+ * <ul>
+ * <li> constructor: creates a new connection object that wraps the given
+ * repository location and connection method.
+ * <li> open: opens a connection.
+ * <li> send a request: use write* method or use the request stream directly.
+ * <code>GetRequestStream</code> returns an output stream to directly
+ * talk to the server.
+ * <li> read responses: use read* methods or use the response stream directly.
+ * <code>GetResponseStream</code> returns an input stream to directly
+ * read output from the server.
+ * <li> close: closes the connection. A closed connection can be reopened by
+ * calling open again.
+ * </ul>
+ */
+public class Connection {
+
+ private static final boolean DEBUG= System.getProperty("cvsclient.debug")!=null;
+ // private static final boolean DEBUG=true;
+
+ public static final byte NEWLINE= 0xA;
+
+ private IServerConnection serverConnection;
+
+ private ICVSRepositoryLocation fCVSRoot;
+ private String fCVSRootDirectory;
+ private boolean fIsEstablished;
+ private BufferedInputStream fResponseStream;
+ private char fLastUsedTokenDelimiter;
+
+ boolean closed = false;
+
+ public Connection(ICVSRepositoryLocation cvsroot, IServerConnection serverConnection) {
+ fCVSRoot = cvsroot;
+ this.serverConnection = serverConnection;
+ }
+
+ private static byte[] append(byte[] buffer, int index, byte b) {
+ if (index >= buffer.length) {
+ byte[] newBuffer= new byte[index * 2];
+ System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
+ buffer= newBuffer;
+ }
+ buffer[index]= b;
+ return buffer;
+ }
+ /**
+ * Closes the connection.
+ */
+ public void close() throws CVSException {
+ if (!isEstablished())
+ return;
+ try {
+ // Perhaps it should be left to the connection to deal with reading pending input!
+ readPendingInput();
+ serverConnection.close();
+ } catch (IOException ex) {
+ throw new CVSCommunicationException(Policy.bind("Connection.cannotClose"), ex);
+ } finally {
+ fResponseStream= null;
+ fIsEstablished= false;
+ }
+ }
+ /**
+ * Flushes the request stream.
+ */
+ public void flush() throws CVSException {
+ if (!isEstablished())
+ return;
+ try {
+ getRequestStream().flush();
+ } catch(IOException e) {
+ throw new CVSCommunicationException(e);
+ }
+ }
+ //---- CVS root management -------------------------------------------------------
+
+ /**
+ * Returns the CVS root.
+ */
+ public ICVSRepositoryLocation getCVSRoot() {
+ return fCVSRoot;
+ }
+
+ /**
+ * Returns the last delimiter character used to read a token.
+ */
+ public char getLastUsedDelimiterToken() {
+ return fLastUsedTokenDelimiter;
+ }
+
+ /**
+ * Returns the <code>OutputStream</code> used to send requests
+ * to the server.
+ */
+ public OutputStream getRequestStream() throws CVSException {
+ if (!isEstablished())
+ return null;
+ return serverConnection.getOutputStream();
+ }
+ /**
+ * Returns the <code>InputStream</code> used to read responses from
+ * the server.
+ */
+ public InputStream getResponseStream() throws CVSException {
+ if (!isEstablished())
+ return null;
+ if (fResponseStream == null)
+ fResponseStream= new BufferedInputStream(serverConnection.getInputStream());
+ return fResponseStream;
+ }
+
+ public String getRootDirectory() throws CVSException {
+ return getCVSRoot().getRootDirectory();
+ }
+
+ /**
+ * Returns <code>true</code> if the connection is established;
+ * otherwise <code>false</code>.
+ */
+ public boolean isEstablished() {
+ return fIsEstablished;
+ }
+ //--- Helper to read strings from server -----------------------------------------
+
+ /**
+ * Is input available in the response stream.
+ */
+ // NIK: is not used
+ public boolean isInputAvailable() {
+ if (!isEstablished())
+ return false;
+ try {
+ return getResponseStream().available() != 0;
+ } catch (CVSException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ public boolean isClosed() {
+ return closed;
+ }
+ /**
+ * Creates a blank separated string from the given string array.
+ */
+ private String makeString(String[] s) {
+ StringBuffer buffer= new StringBuffer();
+ for (int i= 0; i < s.length; i++) {
+ if (i != 0)
+ buffer.append(' ');
+ buffer.append(s[i]);
+ }
+ return buffer.toString();
+ }
+ /**
+ * Opens the connection.
+ */
+ public void open() throws CVSException {
+ if (isEstablished())
+ return;
+ try {
+ serverConnection.open();
+ } catch (IOException e) {
+ throw new CVSCommunicationException(e);
+ }
+ fIsEstablished= true;
+ }
+ /**
+ * Reads a line from the response stream.
+ */
+ public String readLine() throws CVSException {
+ return readLineOrUntil(-1);
+ }
+
+static String readLine(InputStream in) throws IOException {
+ byte[] buffer = new byte[256];
+ int index = 0;
+ int r;
+ while ((r = in.read()) != -1) {
+ if (r == NEWLINE)
+ break;
+ buffer = append(buffer, index++, (byte) r);
+ }
+ String result = new String(buffer, 0, index);
+ if (DEBUG)
+ System.out.println(result);
+ return result;
+}
+
+/**
+ * Low level method to read a token.
+ */
+private String readLineOrUntil(int end) throws CVSException {
+ if (!isEstablished())
+ throw new CVSCommunicationException(Policy.bind("Connection.readUnestablishedConnection"));
+ byte[] buffer = new byte[256];
+ InputStream in = getResponseStream();
+ int index = 0;
+ int r;
+ try {
+ while ((r = in.read()) != -1) {
+ if (r == NEWLINE || (end != -1 && r == end))
+ break;
+ buffer = append(buffer, index++, (byte) r);
+ }
+ switch (r) {
+ case -1 :
+ closed = true;
+ case NEWLINE :
+ fLastUsedTokenDelimiter = '\n';
+ break;
+ default :
+ fLastUsedTokenDelimiter = (char) r;
+ }
+ String result = new String(buffer, 0, index);
+ if (DEBUG)
+ System.out.print(result + fLastUsedTokenDelimiter);
+ return result;
+ } catch (IOException e) {
+ throw new CVSCommunicationException(e);
+ }
+}
+ /**
+ * Reads any pending input from the response stream so that
+ * the stream can savely be closed.
+ */
+ protected void readPendingInput() throws CVSException {
+ byte[] buffer= new byte[2048];
+ InputStream in= getResponseStream();
+ OutputStream out= getRequestStream();
+ try {
+ while (true) {
+ int available = in.available();
+ if (available < 1) break;
+ if (available > buffer.length) available = buffer.length;
+ if (in.read(buffer, 0, available) < 1) break;
+ }
+ out.flush();
+ while (true) {
+ int available = in.available();
+ if (available < 1) break;
+ if (available > buffer.length) available = buffer.length;
+ if (in.read(buffer, 0, available) < 1) break;
+ }
+ } catch (IOException e) {
+ throw new CVSCommunicationException(e);
+ }
+ }
+ /**
+ * Reads a token from the response stream.
+ */
+ public String readToken() throws CVSException {
+ return readLineOrUntil(' ');
+ }
+ /**
+ * Sends the given array of strings to the server. The array's strings
+ * are concatenated using a blank.
+ */
+ public void write(String[] a) throws CVSException {
+ write(makeString(a), false);
+ }
+ //---- Helper to send strings to the server ----------------------------
+
+ /**
+ * Sends the given string to the server.
+ */
+ public void write(String s) throws CVSException {
+ write(s, false);
+ }
+ /**
+ * Sends the given two strings separated by a blank to the
+ * server.
+ */
+ public void write(String s1, String s2) throws CVSException {
+ write(s1 + ' ' + s2, false);
+ }
+ /**
+ * Low level method to write a string to the server. All write* methods are
+ * funneled through this method.
+ */
+ void write(String s, boolean newline) throws CVSException {
+ if (!isEstablished())
+ throw new CVSCommunicationException(Policy.bind("Connection.writeUnestablishedConnection"));
+
+ if (DEBUG)
+ System.out.print(s + (newline ? "\n" : ""));
+
+ try {
+ OutputStream out= getRequestStream();
+ out.write(s.getBytes());
+ if (newline)
+ out.write(NEWLINE);
+ out.flush();
+
+ } catch (IOException e) {
+ throw new CVSCommunicationException(e);
+ }
+ }
+ /**
+ * Sends the given array of strings to the server. The array's strings
+ * are concatenated using a blank. Additionally a newline is sent.
+ */
+ public void writeLine(String[] a) throws CVSException {
+ write(makeString(a), true);
+ }
+ /**
+ * Sends the given string and a newline to the server.
+ */
+ public void writeLine(String s) throws CVSException {
+ write(s, true);
+ }
+ /**
+ * Sends the given two strings separated by a blank to the
+ * server. Additionally a newline is sent.
+ */
+ public void writeLine(String s1, String s2) throws CVSException {
+ write(s1 + ' ' + s2, true);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnection.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnection.java
new file mode 100644
index 000000000..13d771d49
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnection.java
@@ -0,0 +1,253 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.ccvs.core.*;
+import org.eclipse.team.ccvs.core.*;
+
+/**
+ * A connection used to talk to an cvs pserver.
+ */
+public class PServerConnection implements IServerConnection {
+
+ protected static final String SLEEP_PROPERTY = "cvs.pserver.wait";
+ protected static final String milliseconds = System.getProperty(SLEEP_PROPERTY);
+
+ public static final char NEWLINE= 0xA;
+
+ /** default CVS pserver port */
+ private static final int DEFAULT_PORT= 2401;
+
+ /** error line indicators */
+ private static final char ERROR_CHAR = 'E';
+ private static final String ERROR_MESSAGE = "error 0";
+ private static final String NO_SUCH_USER = "no such user";
+
+ private static final char[] SCRAMBLING_TABLE=new char[] {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 114,120,53,79,96,109,72,108,70,64,76,67,116,74,68,87,
+ 111,52,75,119,49,34,82,81,95,65,112,86,118,110,122,105,
+ 41,57,83,43,46,102,40,89,38,103,45,50,42,123,91,35,
+ 125,55,54,66,124,126,59,47,92,71,115,78,88,107,106,56,
+ 36,121,117,104,101,100,69,73,99,63,94,93,39,37,61,48,
+ 58,113,32,90,44,98,60,51,33,97,62,77,84,80,85,223,
+ 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+ 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+ 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+ 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+ 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+ 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+ 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+ 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+ };
+
+ /** Communication strings */
+ private static final String BEGIN= "BEGIN AUTH REQUEST";
+ private static final String END= "END AUTH REQUEST";
+ private static final String LOGIN_OK= "I LOVE YOU";
+ private static final String LOGIN_FAILED= "I HATE YOU";
+
+ private String password;
+ private ICVSRepositoryLocation cvsroot;
+
+ private Socket fSocket;
+
+ private InputStream inputStream;
+ private OutputStream outputStream;
+
+ /**
+ * @see Connection#doClose()
+ */
+ public void close() throws IOException {
+ fSocket.close();
+ fSocket= null;
+ }
+
+ /**
+ * @see Connection#doOpen()
+ */
+ public void open() throws IOException, CVSAuthenticationException {
+
+ // XXX see sleepIfPropertyIsSet() for comments.
+ // This should be removed once we have corrected the
+ // CVS plugin's bad behavior with connections.
+ sleepIfPropertyIsSet();
+
+ fSocket = createSocket();
+ try {
+ this.inputStream = new BufferedInputStream(fSocket.getInputStream());
+ this.outputStream = new BufferedOutputStream(fSocket.getOutputStream());
+ authenticate();
+ } catch (IOException e) {
+ cleanUpAfterFailedConnection();
+ throw e;
+ } catch (CVSAuthenticationException e) {
+ cleanUpAfterFailedConnection();
+ throw e;
+ }
+ }
+
+ /**
+ * @see Connection#getInputStream()
+ */
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+ /**
+ * @see Connection#getOutputStream()
+ */
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Creates a new <code>PServerConnection</code> for the given
+ * cvs root.
+ */
+ PServerConnection(ICVSRepositoryLocation cvsroot, String password) {
+ this.cvsroot = cvsroot;
+ this.password = password;
+ }
+ /**
+ * Does the actual authentification.
+ */
+ private void authenticate() throws IOException, CVSAuthenticationException {
+ String scrambledPassword = scramblePassword(password);
+
+ String user = cvsroot.getUsername();
+ OutputStream out = getOutputStream();
+
+ StringBuffer request = new StringBuffer();
+ request.append(BEGIN);
+ request.append(NEWLINE);
+ request.append(cvsroot.getRootDirectory());
+ request.append(NEWLINE);
+ request.append(user);
+ request.append(NEWLINE);
+ request.append(scrambledPassword);
+ request.append(NEWLINE);
+ request.append(END);
+ request.append(NEWLINE);
+ out.write(request.toString().getBytes());
+ out.flush();
+ String line = Connection.readLine(getInputStream());
+
+ // Return if we succeeded
+ if (LOGIN_OK.equals(line))
+ return;
+
+ // Otherwise, determine the type of error
+ if (line.length() == 0)
+ throw new IOException(Policy.bind("PServerConnection.noResponse"));
+ if (LOGIN_FAILED.equals(line))
+ throw new CVSAuthenticationException(cvsroot.getLocation(), Policy.bind("PServerConnection.loginRefused"));
+ String message = "";
+ // Skip any E messages for now
+ while (line.charAt(0) == ERROR_CHAR) {
+ // message += line.substring(1) + " ";
+ line = Connection.readLine(getInputStream());
+ }
+ // Remove leading "error 0"
+ if (line.startsWith(ERROR_MESSAGE))
+ message += line.substring(ERROR_MESSAGE.length() + 1);
+ else
+ message += line;
+ if (message.indexOf(NO_SUCH_USER) != -1)
+ throw new CVSAuthenticationException(cvsroot.getLocation(), Policy.bind("PServerConnection.invalidUser", new Object[] {message}));
+ throw new IOException(Policy.bind("PServerConnection.connectionRefused", new Object[] { message }));
+ }
+ /*
+ * Called if there are exceptions when connecting.
+ * This method makes sure that all connections are closed.
+ */
+ private void cleanUpAfterFailedConnection() throws IOException {
+ try {
+ if (inputStream != null)
+ inputStream.close();
+ } finally {
+ try {
+ if (outputStream != null)
+ outputStream.close();
+ } finally {
+ try {
+ if (fSocket != null)
+ fSocket.close();
+ } finally {
+ fSocket = null;
+ }
+ }
+ }
+
+ }
+ /**
+ * Creates the actual socket
+ */
+ protected Socket createSocket() throws IOException {
+ // Determine what port to use
+ int port = cvsroot.getPort();
+ if (port == cvsroot.USE_DEFAULT_PORT)
+ port = DEFAULT_PORT;
+ // Make the connection
+ Socket result;
+ try {
+ result= new Socket(cvsroot.getHost(), port);
+ } catch (InterruptedIOException e) {
+ // If we get this exception, chances are the host is not responding
+ throw new InterruptedIOException(Policy.bind("PServerConnection.socket", new Object[] {cvsroot.getHost()}));
+ }
+ result.setSoTimeout(cvsroot.getTimeout() * 1000);
+ return result;
+ }
+
+ private String scramblePassword(String password) throws CVSAuthenticationException {
+ int length = password.length();
+ char[] out= new char[length];
+ for (int i= 0; i < length; i++) {
+ char value = password.charAt(i);
+ if( value < 0 || value > 255 )
+ throwInValidCharacter();
+ out[i]= SCRAMBLING_TABLE[value];
+ }
+ return "A" + new String(out);
+ }
+
+ private void throwInValidCharacter() throws CVSAuthenticationException {
+ throw new CVSAuthenticationException(cvsroot.getLocation(),
+ Policy.bind("PServerConnection.invalidChars"));
+ }
+
+ /**
+ * XXX This is provided to allow slowing down of pserver connections in cases
+ * where the inetd connections per second setting is not set high enough. The
+ * CVS plugin has a known problem of creating too many unnecessary connections.
+ */
+ private void sleepIfPropertyIsSet()
+ {
+ try {
+ if( milliseconds == null )
+ return;
+
+ long sleepMilli = new Long(milliseconds).longValue();
+
+ if( sleepMilli > 0 )
+ Thread.currentThread().sleep(sleepMilli);
+ } catch( InterruptedException e ) {
+ // keep going
+ } catch( NumberFormatException e ) {
+ // don't sleep if number format is wrong
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnectionMethod.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnectionMethod.java
new file mode 100644
index 000000000..1bd7a5f70
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/PServerConnectionMethod.java
@@ -0,0 +1 @@
+package org.eclipse.team.internal.ccvs.core.connection; /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ import org.eclipse.team.ccvs.core.ICVSRepositoryLocation; import org.eclipse.team.ccvs.core.IConnectionMethod; import org.eclipse.team.ccvs.core.IServerConnection; public class PServerConnectionMethod implements IConnectionMethod { /** * @see IConnectionMethod#createConnection(ICVSRepositoryLocation, String) */ public IServerConnection createConnection(ICVSRepositoryLocation location, String password) { return new PServerConnection(location, password); } /** * @see IConnectionMethod#getName() */ public String getName() { return "pserver"; } } \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/ResourceStatus.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/ResourceStatus.java
new file mode 100644
index 000000000..cf07cb05b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/connection/ResourceStatus.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.internal.ccvs.core.connection;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+/**
+ *
+ */
+public class ResourceStatus extends Status {
+
+ private IPath path;
+
+
+ public ResourceStatus(
+ int type,
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ super(type, CVSProviderPlugin.ID, code, message, exception);
+ this.path = path;
+ }
+ public ResourceStatus(int code, String message) {
+ this(getSeverity(code), code, null, message, null);
+ }
+ public ResourceStatus(int code, IPath path, String message) {
+ this(getSeverity(code), code, path, message, null);
+ }
+ public ResourceStatus(
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ this(getSeverity(code), code, path, message, exception);
+ }
+ public IPath getPath() {
+ return path;
+ }
+ protected static int getSeverity(int code) {
+ return code;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
new file mode 100644
index 000000000..6d7ecf779
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
@@ -0,0 +1,84 @@
+org.eclipse.team.internal.provider.cvs.CVSException=CVS Error: {0}
+
+CVSAuthenticationException.normal=Authentication error connecting to {0}
+CVSAuthenticationException.detail=Authentication error: {0}
+
+CVSCommunicationException.io=CVS communication error: {0}
+CVSFileException.io=Error accessing CVS file
+CVSDiffException.message=The compared files are different
+
+java.io.IOException={0}
+java.io.EOFException=End of file encountered: {0}
+java.io.FileNotFoundException=File not found: {0}
+java.io.InterruptedIOException=Interrupted IO: {0}
+java.net.UnknownHostException=Cannot locate host: {0}
+
+Connection.cannotClose=Cannot close connection
+
+PServerConnection.invalidChars=Invalid characters in password
+PServerConnection.noUser=No user specified
+PServerConnection.hostInvalid=Invalid host
+PServerConnection.loginRefused=Incorrect user name or password
+PServerConnection.invalidUser={0}
+PServerConnection.host=Cannot locate host: {0}
+PServerConnection.socket=Cannot connect to host: {0}
+PServerConnection.connectionRefused=Connection refused: {0}
+PServerConnection.stream=Error opening socket connection
+PServerConnection.noResponse=No response from server
+
+CVSProviderPlugin.cannotUpdateDescription=Error updating project description
+
+CVSTeamProvider.deconfigureProblem=Error while deconfiguring CVS project {0}
+CVSTeamProvider.initializationFailed=Initialization of CVS for project {0} failed
+CVSTeamProvider.visitError=An error occurred while visiting resource {0}
+CVSTeamProvider.invalidResource=Resource {0} is not a child of project {1}
+CVSTeamProvider.checkinProblems
+
+ProjectDescriptionManager.unableToReadDescription=An error occured reading the project description
+ProjectDescriptionManager.ioDescription=An IO error occured while writing the project description
+ProjectDescriptionManager.coreDescription=A Core error occured while writing the project description
+
+ResourceDeltaVisitor.visitError=Error while processing resource deltas
+
+ResponseDispatcher.serverError=The CVS server responded with an error (see the CVS console)
+ResponseDispatcher.receiving=Receiving reponse
+
+FileProperties.invalidEntryLine=Invalid entry line: {0}
+
+ManagedFile.receiving=Receiving file {0}
+ManagedFile.sending=Sending file {0}
+ManagedFile.transfer={0} ({1}K of {2}K bytes)
+
+RemoteManagedResource.invalidOperation=Invalid operation performed on remote resource
+RemoteManagedFolder.invalidChild=Invalid folder {0} received during remote operation
+
+Command.server=Contacting server
+Command.invalidRoot=Resource {0} is not a valid CVS root directory
+Command.invalidResource=Resource {0} is not a valid CVS resource
+
+RequestSender.sendModified=Sending modified file {0}
+
+Response.problemsReported=The CVS server reported an error (See the CVS console)
+
+DefaultHandler.connectionClosed=The connection to the server has been closed
+ModTimeHandler.invalidFormat=The server modification time {0} is in an unknown format
+Updated.numberFormat=Server did not send length of the file
+UnsupportedHandler.message=Unsupported response received from server
+
+CVSRepositoryLocation.nullLocation=Location must not be null
+CVSRepositoryLocation.emptyLocation=Location must not be empty
+CVSRepositoryLocation.endWhitespace=Location must not end with whitespace
+CVSRepositoryLocation.locationForm=Location must have form ':methodname:[user[:password]@]host[#port]:/path/to/cvsroot'
+CVSRepositoryLocation.startOfLocation=Location must start with a connection method name enclosed in colons
+CVSRepositoryLocation.methods=Only the following methods are supported: {0}
+CVSRepositoryLocation.parsingMethod=Error in connection method specification
+CVSRepositoryLocation.parsingUser=Error in user name specification
+CVSRepositoryLocation.parsingPassword=Error in password specification
+CVSRepositoryLocation.parsingHost=Error in host specification
+CVSRepositoryLocation.parsingPort=Error in port specification
+CVSRepositoryLocation.parsingRoot=Error in repository root directory specification
+CVSRepositoryLocation.invalidFormat=Invalid CVS repository location format: {0}
+
+ProjectDescriptionContentHandler.xml=Error parsing project description file
+
+Util.invalidResource=Resource {1} is not relative to root {0}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/RequestSender.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/RequestSender.java
new file mode 100644
index 000000000..fb962d83e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/RequestSender.java
@@ -0,0 +1,258 @@
+package org.eclipse.team.internal.ccvs.core.requests;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.StringTokenizer;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.response.IResponseHandler;
+
+/**
+ * The reqest-sender is the only way to send messages to the
+ * server.
+ *
+ * It has a lot of helper-methods like "sendGlobalOption, sendEntry ..."
+ * this do send messages with parameter up to the server. These ways of
+ * sending are discibed in the cvs-protocol specification
+ *
+ */
+public class RequestSender {
+
+ /** Requests that don't expect any response from the server */
+ public static final String ARGUMENT = "Argument";
+ public static final String ARGUMENTX = "Argumentx";
+ public static final String DIRECTORY = "Directory";
+ public static final String ENTRY = "Entry";
+ public static final String GLOBAL_OPTION = "Global_option";
+ public static final String ROOT = "Root";
+ public static final String UNCHANGED = "Unchanged";
+ public static final String VALID_RESPONSES = "Valid-responses";
+ public static final String QUESTIONABLE = "Questionable";
+ public static final String KOPT = "Kopt";
+
+ /** Requests that do expect any response from the server */
+ public static final String CHECKOUT = "co";
+ public static final String IMPORT = "import";
+ public static final String VALID_REQUESTS = "valid-requests";
+ public static final String EXPAND_MODULES = "expand-modules";
+ public static final String CI = "ci";
+ public static final String MODIFIED = "Modified";
+ public static final String IS_MODIFIED = "Is-modified";
+ public static final String STATUS = "status";
+ public static final String UPDATE = "update";
+ public static final String HISTORY = "history";
+ public static final String ADD = "add";
+ public static final String REMOVE = "remove";
+ public static final String LOG = "log";
+ public static final String RTAG = "rtag";
+ public static final String TAG = "tag";
+ public static final String DIFF = "diff";
+ public static final String ADMIN = "admin";
+ public static final String STICKY = "Sticky";
+
+ /** Helper Constants that are not going to be send to server */
+ public static final String SERVER_SEPERATOR = "/";
+ private static final String EMPTY_LOCAL_FOLDER = ".";
+ private static final String LINEFEED = "\n";
+ private static final String CRETURN = "\r";
+ private static final String STANDARD_PERMISSION = "u=rw,g=rw,o=r";
+
+ /**
+ * The link to the server to send things out
+ */
+ private Connection connection;
+
+ /**
+ * List of the valid-request as stated from the
+ * server.
+ * For future checking on that.
+ */
+ private String validRequests;
+
+ /**
+ * Constructor that takes the connection
+ */
+ public RequestSender (Connection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Is the given request a valid server request.
+ */
+ public boolean isValidRequest(String requestName) {
+ if (validRequests == null)
+ return false;
+ return validRequests.indexOf(requestName) != -1;
+ }
+
+ /**
+ * Set the list of valid-request when you get
+ * the list of valid request from the server.
+ */
+ void setValidRequest(String validRequests) {
+ this.validRequests = validRequests;
+ }
+
+ /**
+ * Get a Handler for the "valid-request", that does
+ * collect the information to this class.
+ */
+ public IResponseHandler getValidRequestHandler() {
+ return new ValidRequestHandler(this);
+ }
+
+ /**
+ * This is the general way to send text to the server.
+ * Most commonly it is used to send a single constant
+ * to the server
+ */
+ public void writeLine(String data) throws CVSException {
+ connection.writeLine(data);
+ }
+
+ /**
+ * Sends an argument to the server. If arg contains newlines
+ * of any kind the argument as one first argument and after
+ * that as argument extentions.<br>
+ * E.g.: sendArgument("Hello\nWorld\nHello\r World") is send as
+ * <ul>
+ * <li> Argument Hello
+ * <li> Argumentx World
+ * <li> Argumentx Hello
+ * <li> Argumentx World
+ * </ul>
+ */
+ public void sendArgument(String arg) throws CVSException {
+
+ StringTokenizer tokenizer;
+
+ if (arg.indexOf(LINEFEED) == -1 &&
+ arg.indexOf(CRETURN) == -1) {
+ connection.writeLine(ARGUMENT, arg);
+ return;
+ }
+
+ // Create a tokenizer, that uses all newline-caracters as
+ // delimitor
+ tokenizer = new StringTokenizer(arg,LINEFEED + CRETURN);
+
+ // We do not want an argument with a newlines only
+ Assert.isTrue(tokenizer.hasMoreTokens());
+
+ connection.writeLine(ARGUMENT, tokenizer.nextToken());
+ while (tokenizer.hasMoreTokens()) {
+ connection.writeLine(ARGUMENTX, tokenizer.nextToken());
+ }
+ }
+
+ public void sendKopt(String arg) throws CVSException {
+ connection.writeLine(KOPT, arg);
+ }
+
+ public void sendIsModified(String file) throws CVSException {
+ connection.writeLine(IS_MODIFIED, file);
+ }
+
+ /**
+ * The Directory request is sent as:
+ * <ul>
+ * <li>Directory localdir
+ * <li>repository_root/remotedir
+ * </ul>
+ *
+ * This note is copied from an old version:
+ * [Note: A CVS repository root can end with a trailing slash. The CVS server
+ * expects that the repository root sent contain this extra slash. Including
+ * the foward slash in addition to the absolute remote path makes for a string
+ * containing two consecutive slashes (e.g. /home/cvs/repo//projecta/a.txt).
+ * This is valid in the CVS protocol.]
+ */
+ public void sendConstructedDirectory(String local, String remote) throws CVSException {
+
+ // FIXME I do not know wether this method is "ModuleFile-safe"
+
+ connection.writeLine(DIRECTORY, local);
+ connection.writeLine(connection.getRootDirectory() +
+ SERVER_SEPERATOR + remote);
+ }
+
+ /**
+ * The Directory request is sent as:
+ * <ul>
+ * <li>Directory localdir
+ * <li>repository_root/remotedir
+ * </ul>
+ */
+ public void sendDirectory(String local, String remote) throws CVSException {
+
+ if (local.equals("")) {
+ local = EMPTY_LOCAL_FOLDER;
+ }
+
+ connection.writeLine(DIRECTORY, local);
+ connection.writeLine(remote);
+ }
+
+ public void sendEntry(String entryLine) throws CVSException {
+ connection.writeLine(ENTRY, entryLine);
+ }
+
+ public void sendGlobalOption(String option) throws CVSException {
+ connection.writeLine(GLOBAL_OPTION, option);
+ }
+
+ public void sendUnchanged(String filename) throws CVSException {
+ connection.writeLine(UNCHANGED, filename);
+ }
+
+ public void sendQuestionable(String filename) throws CVSException {
+ connection.writeLine(QUESTIONABLE, filename);
+ }
+
+ public void sendSticky(String tag) throws CVSException {
+ connection.writeLine(STICKY, tag);
+ }
+
+ /**
+ * This does not only send the message to the server that the
+ * file is going to be uploaded.<br>
+ * It does also acctually upload the file.<br>
+ * NOTE: The entry line has to be send before calling this method
+ */
+ public void sendModified(IManagedFile file, IProgressMonitor monitor, boolean binary)
+ throws CVSException {
+
+ // boolean binary;
+
+ // Send
+ // - MODIFIED
+ // - permissions
+ // - size
+ // - Content of the file
+
+ // Does not send the entryLinde !!
+ connection.writeLine(MODIFIED, file.getName());
+
+ if (file.getFileInfo() == null ||
+ file.getFileInfo().getPermissions() == null) {
+ connection.writeLine(STANDARD_PERMISSION);
+ } else {
+ connection.writeLine(file.getFileInfo().getPermissions());
+ }
+
+ String progressTitle =
+ Policy.bind("RequestSender.sendModified", file.getName());
+ monitor.subTask(progressTitle);
+ file.sendTo(connection.getRequestStream(),monitor,binary);
+ }
+
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/ValidRequestHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/ValidRequestHandler.java
new file mode 100644
index 000000000..c043b0aa0
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/requests/ValidRequestHandler.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.internal.ccvs.core.requests;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.ResponseHandler;
+
+
+/**
+ * Takes the valid-requsts out of the stream and puts
+ * it into the RequestSender.
+ * Checking whether requests are acctually valid is to
+ * be done in the future.
+ */
+class ValidRequestHandler extends ResponseHandler {
+
+ RequestSender requestSender;
+
+ public ValidRequestHandler(RequestSender requestSender) {
+ this.requestSender = requestSender;
+ }
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Valid-requests";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream monitor,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ // Set the ValidRequests of the requestSender
+ requestSender.setValidRequest(connection.readLine());
+
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFile.java
new file mode 100644
index 000000000..7fee37e5e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFile.java
@@ -0,0 +1,190 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+
+
+/**
+ * Implement the abstract fileSystem
+ * @see CVSResource
+ */
+class CVSFile extends CVSResource implements ICVSFile {
+
+ // The ioResource is saved in CVSResource and used from there
+ // private File file;
+
+ CVSFile(String path) throws CVSException {
+ this(new File(path));
+ }
+
+
+ CVSFile(File ioFile) {
+ // puts the file into resource
+ super(ioFile);
+
+ Assert.isTrue(!ioFile.exists() || ioFile.isFile());
+ }
+
+
+ /**
+ * @see ICVSFile#getInputStream()
+ */
+ public InputStream getInputStream() throws CVSException {
+
+ exceptionIfNotExist();
+
+ try {
+ return new FileInputStream(ioResource);
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ }
+
+
+ /**
+ * Acctuall creation of a new file. (Does not have to exist before)
+ * This is used from outside to create a file at a location.
+ *
+ * All checks, and creaton of files are done here
+ */
+ static ICVSFile createFileFrom(String path) throws CVSException {
+ return createFileFrom(new File(path));
+ }
+
+
+ /**
+ * @see CVSFile#createFileFromPath(String)
+ */
+ public static ICVSFile createFileFrom(File newFile) throws CVSException {
+
+ if (!newFile.getParentFile().exists()) {
+ throw new CVSException("You tried to create a file in an non-existing Folder");
+ }
+
+ try {
+ newFile = newFile.getCanonicalFile();
+ // newFile.createNewFile();
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ return new CVSFile(newFile);
+ }
+
+ /**
+ * @see ICVSFile#getOutputStream()
+ */
+ public OutputStream getOutputStream() throws CVSException {
+
+ // If the file is read-only we need to delete it before
+ // we can write it new
+ deleteIfProtected(ioResource);
+
+ // No CVSException should happen here, unless
+ // the underlying system is not O.K.
+ try {
+ return new FileOutputStream(ioResource);
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ }
+
+
+ /**
+ * @see ICVSFile#getSize()
+ */
+ public long getSize() {
+
+ return ioResource.length();
+ }
+
+ /**
+ * @see ICVSResource#delete()
+ */
+ public void delete() {
+ super.delete();
+ }
+
+ /**
+ * @see ICVSResource#isFolder()
+ */
+ public boolean isFolder() {
+ return false;
+ }
+
+
+ /**
+ * @see ICVSFile#getTimeStamp()
+ */
+ public long getTimeStamp() {
+ return ioResource.lastModified();
+ }
+
+
+ /**
+ * @see ICVSFile#setTimeStamp(Date)
+ */
+ public void setTimeStamp(long msec) {
+ ioResource.setLastModified(msec);
+ }
+
+ /**
+ * @see ICVSFile#getContent()
+ */
+ public String[] getContent() throws CVSException {
+ return readFromFile(ioResource);
+ }
+
+ /**
+ * @see ICVSFile#setContent(String[], boolean)
+ */
+// public void setContent(String[] content, String delim)
+// throws CVSException {
+//
+// writeToFile(ioResource, content, delim);
+// }
+
+ /**
+ * @see ICVSFile#moveTo(ICVSFile)
+ */
+ public void moveTo(ICVSFile file) throws CVSException {
+
+ boolean success;
+
+ success = ioResource.renameTo(new File(file.getPath()));
+
+ if (!success) {
+ throw new CVSException("Move from " + ioResource + " to " + file + " was not possible");
+ }
+ }
+
+ /**
+ * @see ICVSResource#clearCache()
+ */
+ public void clearCache(boolean deep) throws CVSException {
+ // getParent().clearCache(boolean deep);
+ }
+
+ public void setReadOnly() {
+
+ boolean sucess;
+ sucess = ioResource.setReadOnly();
+ Assert.isTrue(sucess);
+
+ }
+
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFolder.java
new file mode 100644
index 000000000..db3962501
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSFolder.java
@@ -0,0 +1,479 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.NotCVSFolderException;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.util.ListFileFilter;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+
+/**
+ * Implement the abstract fileSystem
+ * @see CVSResource
+ */
+class CVSFolder extends CVSResource implements ICVSFolder {
+
+ // The ioFolder is stored in the ioResource of the
+ // superclass CVSResource
+ // -- private File ioFolder;
+
+ public static final String CVS_FOLDER_NAME = "CVS";
+ public static final boolean PROPERTY_READ_CHACHING = true;
+
+ // If we do not extend the key and therefore the key is the same like
+ // the absolut pathname we have indirectly an reference to the key in
+ // the weak hashmap. Therefore the WeakHashMap does not finalize anything
+ private static final String KEY_EXTENTION = "KEY";
+
+ // We could use a normal HashMap in case the caller does not have instances
+ // for all the time it needs the object
+ private static Map instancesCache = new HashMap();
+ private HashMap propertiesCache = new HashMap();
+
+ /**
+ * NOT to be called (directly). Use createInternalFolder indtead.
+ */
+ private CVSFolder(File ioFolder) {
+ // puts the file into resource
+ super(ioFolder);
+
+ Assert.isTrue(ioFolder == null || !ioFolder.exists() || ioFolder.isDirectory());
+ }
+
+ /**
+ * @see ICVSFolder#getFolders()
+ */
+ public ICVSFolder[] getFolders() throws CVSException {
+
+ File[] folderList;
+ ICVSFolder[] cvsFolderList;
+
+ // Get all folder without the cvs-folder
+ folderList = ioResource.listFiles(new FoFilter());
+ cvsFolderList = new ICVSFolder[folderList.length];
+
+
+ for (int i = 0; i<folderList.length; i++) {
+ cvsFolderList[i] = createInternalFolderFrom(folderList[i]);
+ }
+ return cvsFolderList;
+ }
+
+
+ /**
+ * @see ICVSFolder#getFiles()
+ */
+ public ICVSFile[] getFiles() {
+
+ File[] fileList;
+ ICVSFile[] cvsFileList;
+
+ // Get all files
+ fileList = ioResource.listFiles(new FiFilter());
+ cvsFileList = new ICVSFile[fileList.length];
+
+ for (int i = 0; i<fileList.length; i++) {
+ cvsFileList[i] = new CVSFile(fileList[i]);
+ }
+ return cvsFileList;
+ }
+
+ /**
+ * Does list the whole content of the folder
+ * (files and folders)
+ */
+ public ICVSResource[] getResources() throws CVSException {
+
+ File[] resourceList;
+ ICVSResource[] cvsResourceList;
+
+ exceptionIfNotExist();
+
+ // Get all resources
+ resourceList = ioResource.listFiles(new NoCVSFilter());
+
+
+ cvsResourceList = new ICVSResource[resourceList.length];
+
+ for (int i = 0; i<resourceList.length; i++) {
+ if (resourceList[i].isDirectory()) {
+ cvsResourceList[i] = createInternalFolderFrom(resourceList[i]);
+ } else {
+ cvsResourceList[i] = new CVSFile(resourceList[i]);
+ }
+ }
+
+ return cvsResourceList;
+ }
+
+
+ /**
+ * @see ICVSFolder#createFolder(String)
+ */
+ public ICVSFolder createFolder(String name) throws CVSException {
+ return createFolderFrom(new File(ioResource, convertSeparator(name)));
+ }
+
+ /**
+ * Acctuall creation of a new folder. (Does not have to exist before)
+ * This is used from outside to create a folder at a location.
+ *
+ * Here is checked, wether the file we try to create exists
+ * and if we try to create somthing unallowed (e.g. the cvs-folder)
+ */
+ /*
+ public static ICVSFolder createFolderFrom(String path) throws CVSException {
+ return createFolderFrom(new File(convertSeparator(path)));
+ }
+ */
+ /**
+ * @see CVSFolder#createFolderFrom(String)
+ */
+ public static ICVSFolder createFolderFrom(File newFolder) throws CVSException {
+
+ try {
+ newFolder = newFolder.getCanonicalFile();
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+
+ if (newFolder.getName().toUpperCase().equals(CVS_FOLDER_NAME)) {
+ throw new CVSException("You are not allowed to create the CVS-Folder");
+ } else {
+ return createInternalFolderFrom(newFolder);
+ }
+ }
+
+ static CVSFolder createInternalFolderFrom(File newFolder) throws CVSException {
+
+ CVSFolder resultFolder;
+
+ try {
+ newFolder = newFolder.getCanonicalFile();
+ } catch (IOException e) {
+ throw new CVSException(Policy.bind("CVSFolder.invalidPath"),e);
+ // Util.logError(Policy.bind("CVSFolder.invalidPath"),e);
+ }
+
+ resultFolder = (CVSFolder) instancesCache.get(newFolder.getAbsolutePath()+KEY_EXTENTION);
+
+ if (resultFolder == null) {
+ resultFolder = new CVSFolder(newFolder);
+ instancesCache.put(resultFolder.ioResource.getAbsolutePath()+KEY_EXTENTION,resultFolder);
+ }
+
+ return resultFolder;
+ }
+
+ /**
+ * @see ICVSFolder#createFile(String)
+ *
+ */
+ public ICVSFile createFile(String name) throws CVSException {
+
+ // No converting of the seperators here
+ // this function does not work on subfolders anyway
+ return CVSFile.createFileFrom(new File(ioResource, name));
+
+ }
+
+
+ /**
+ * @see ICVSFolder#isCVSFolder()
+ */
+ public boolean isCVSFolder() throws CVSFileNotFoundException {
+
+ exceptionIfNotExist();
+
+
+ return (new File(ioResource, CVS_FOLDER_NAME)).exists();
+
+ }
+
+
+ /**
+ * @see ICVSFolder#makeCVSFolder()
+ */
+ public void makeCVSFolder() throws CVSException {
+
+ exceptionIfNotExist();
+
+ (new File(ioResource, CVS_FOLDER_NAME)).mkdir();
+
+ }
+
+ /**
+ * Throw an exception if the folder in no cvs-folder
+ */
+ private File getCVSFolder() throws NotCVSFolderException, CVSFileNotFoundException {
+
+ if (!isCVSFolder()) {
+ throw new NotCVSFolderException("You tried to do an cvs-operation on a non cvs-folder");
+ }
+
+ return new File(ioResource, CVS_FOLDER_NAME);
+ }
+
+ /**
+ * @see ICVSFolder#setProperty(String, String[])
+ */
+ public void setProperty(String key, String[] content) throws CVSException {
+
+ File cvsFolder;
+ File propertyFile;
+
+ // If we have got a property that is null,
+ // then it is acctually an unset property
+ if (content == null) {
+ unsetProperty(key);
+ return;
+ }
+
+ Assert.isTrue(content.length == 0 || content[0]!=null);
+
+ cvsFolder = getCVSFolder();
+ propertyFile = new File(cvsFolder, key);
+
+ writeToFile(propertyFile,content);
+
+ if (PROPERTY_READ_CHACHING) {
+ propertiesCache.put(key, content);
+ }
+ }
+
+
+ /**
+ * @see ICVSFolder#unsetProperty(String)
+ */
+ public void unsetProperty(String key) throws CVSException {
+ File cvsFolder;
+
+ cvsFolder = getCVSFolder();
+ (new File(cvsFolder, key)).delete();
+
+ if (PROPERTY_READ_CHACHING) {
+ propertiesCache.put(key, null);
+ }
+ }
+
+
+ /**
+ * @see ICVSFolder#getProperty(String)
+ */
+ public String[] getProperty(String key)
+ throws NotCVSFolderException, CVSException {
+
+ String[] property;
+ File cvsFolder;
+ File propertyFile;
+
+ if (PROPERTY_READ_CHACHING && propertiesCache.containsKey(key)) {
+ return (String[])propertiesCache.get(key);
+ }
+
+ cvsFolder = getCVSFolder();
+ propertyFile = new File(cvsFolder, key);
+
+ // If the property does not exsist we return null
+ // this is specified
+ if (propertyFile.exists()) {
+ property = readFromFile(propertyFile);
+ } else {
+ property = null;
+ }
+
+ if (PROPERTY_READ_CHACHING) {
+ // propertiesCache.remove(key);
+ propertiesCache.put(key, property);
+ }
+
+ return property;
+ }
+
+ /**
+ * The oposite of makeCVSFolder,
+ * does delete the whoole CVS folder
+ */
+ public void unmakeCVSFolder() {
+
+ File[] fileList;
+
+ try {
+
+ if (!isCVSFolder()) {
+ return;
+ }
+
+ fileList = getCVSFolder().listFiles();
+ for (int i = 0; i < fileList.length; i++) {
+ fileList[i].delete();
+ }
+ getCVSFolder().delete();
+ } catch (CVSException e) {
+ // Do nothing. We are no cvs-folder
+ // and that is were we want to be
+ }
+
+ }
+
+ /**
+ * @see ICVSResource#delete()
+ */
+ public void delete() {
+
+ // If there is nothing to delete return
+ if (!ioResource.exists()) {
+ return;
+ }
+
+ ICVSResource[] resourceList;
+
+ try {
+ resourceList = getResources();
+ } catch (CVSException e) {
+ // If the file has been deletet in between we
+ // stop executing
+ return;
+ }
+
+ for (int i = 0; i < resourceList.length; i++) {
+ resourceList[i].delete();
+ }
+
+ unmakeCVSFolder();
+ super.delete();
+
+ instancesCache.remove(ioResource.getAbsolutePath()+KEY_EXTENTION);
+ }
+
+
+ /**
+ * @see ICVSResource#isFolder()
+ */
+ public boolean isFolder() {
+ return true;
+ }
+
+ /**
+ * @see ICVSFolder#getChild(IPath)
+ */
+ public ICVSResource getChild(String path) throws CVSException {
+ File file;
+
+ file = new File(ioResource, convertSeparator(path));
+
+ if (!childExists(path)) {
+ throw new CVSFileNotFoundException(getPath() + "/" + path + " does not exist");
+ }
+
+ if (file.isDirectory()) {
+ return createInternalFolderFrom(file);
+ } else {
+ return new CVSFile(file);
+ }
+ }
+
+
+ /**
+ * @see ICVSFolder#childExists(String)
+ */
+ public boolean childExists(String name) {
+ return (new File(ioResource,name)).exists();
+ }
+
+
+ /**
+ * @see ICVSFolder#childIsFolder(String)
+ */
+ public boolean childIsFolder(String name) {
+ return (new File(ioResource,name)).isDirectory();
+ }
+
+
+ /**
+ * @see ICVSFolder#mkdir()
+ */
+ public void mkdir() throws CVSException {
+
+ boolean success;
+
+ success = ioResource.mkdir();
+ if (!success && !exists()) {
+ throw new CVSException("Folder-Creation failed: " + getName());
+ }
+ }
+
+ /**
+ * @see ICVSResource#clearCache()
+ */
+ public void clearCache(boolean deep) throws CVSException {
+
+ ICVSResource[] resources;
+
+ // Do that first, maybe we have got wrong entries
+ // cached
+ propertiesCache = new HashMap();
+
+ if (!deep) {
+ return;
+ }
+
+ resources = getResources();
+
+ for (int i = 0; i < resources.length; i++) {
+ resources[i].clearCache(true);
+ }
+
+ }
+
+}
+
+
+/**
+ * Does filter that you get files back (and no folders)
+ */
+class FiFilter implements FileFilter {
+ public boolean accept(File file) {
+ return file.isFile();
+ }
+}
+
+
+/**
+ * Does filter that you get folders back (and no files)
+ * Does not give the folder called
+ * cvs (no matter wether lowcase or upcase back)
+ */
+class FoFilter extends ListFileFilter {
+ public FoFilter() {
+ // get all folders, that are not the cvs-folder
+ super(new String[]{CVSFolder.CVS_FOLDER_NAME},true,false,true);
+ }
+}
+
+
+/**
+ * Gives you every Resouce but the CVS-Folder back
+ */
+class NoCVSFilter extends ListFileFilter {
+ public NoCVSFilter() {
+ // get all that is not the cvs-folder
+ super(new String[]{CVSFolder.CVS_FOLDER_NAME},true);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSResource.java
new file mode 100644
index 000000000..481d979b6
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/CVSResource.java
@@ -0,0 +1,297 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * CVSResource, CVSFile, CVSFolder implement the
+ * ICVSResource, ICVSFile, ICVSFolder interfaces that are needed
+ * to use the cvs-client.
+ *
+ * Just call CVSFolder.createFolderFromPath(String path) to create an
+ * ICVSFolder and pass it to the client.
+ *
+ * @see CVSFolder#createFolderFromPath(String)
+ * @see CVSFolder
+ * @see CVSFile
+ * @see CVSResource
+ * @see ICVSFolder
+ * @see ICVSFile
+ * @see ICVSResource
+ */
+abstract class CVSResource implements ICVSResource, Comparable {
+
+ public static final String PLATFORM_NEWLINE = System.getProperty("line.separator");
+
+ File ioResource;
+
+ CVSResource(String path) {
+ this(new File(path));
+ }
+
+
+ CVSResource(File ioResource) {
+ this.ioResource = ioResource;
+ }
+
+
+ /**
+ * @see ICVSResource#getName()
+ */
+ public String getName() {
+
+// String path;
+// int lastFileSeperatorPos;
+// String name;
+//
+// path = ioResource.getAbsolutePath();
+// lastFileSeperatorPos = path.lastIndexOf(File.separator);
+// // check that
+// name = path.substring(lastFileSeperatorPos + 1);
+
+ return ioResource.getName();
+ }
+
+ /**
+ * @see ICVSResource#getPath()
+ */
+ public String getPath() {
+ return ioResource.getAbsolutePath();
+ }
+
+
+ /**
+ * @see ICVSResource#delete()
+ */
+ public void delete() {
+ ioResource.delete();
+ }
+
+
+ /**
+ * @see ICVSResource#getParent()
+ */
+ public ICVSFolder getParent() {
+
+ try {
+ return CVSFolder.createInternalFolderFrom(ioResource.getParentFile());
+ } catch (CVSException e) {
+ // This should not happen, because the canonical Path of
+ // a parent should be O.K.
+ Util.logError(Policy.bind("CVSFolder.invalidPath"),e);
+ Assert.isTrue(false);
+ return null;
+ }
+ }
+
+ /**
+ * Equals is equals on the abstract pathnames
+ */
+ public boolean equals(Object obj) {
+
+ if (!(obj instanceof CVSResource)) {
+ return false;
+ } else {
+ return ((CVSResource) obj).getPath().equals(getPath());
+ }
+ }
+
+ /**
+ * Generate a Standard CVSException for an
+ * IOException
+ */
+ protected static CVSException wrapException(IOException e) {
+ return new CVSException(IStatus.ERROR,
+ CVSException.IO_FAILED,
+ "An IOException occured while using your file-system.",
+ e);
+ }
+
+ /** Clean up fileName. "/" and "\" are both replaced for
+ * File.seperator
+ */
+ protected static String convertSeparator(String path) {
+ if (File.separatorChar == '/') {
+ return path.replace('\\','/');
+ } else {
+ return path.replace('/','\\');
+ }
+ }
+
+ /**
+ * Give the pathname back
+ */
+ public String toString() {
+ return getPath();
+ }
+
+ /**
+ * @see ICVSResource#isFolder()
+ */
+ public boolean isFolder() {
+ return false;
+ }
+
+
+ /**
+ * @see ICVSResource#exists()
+ */
+ public boolean exists() {
+ return ioResource.exists();
+ }
+
+ /**
+ * throw an exeption, if the underlying resource
+ * does not exist.
+ */
+ void exceptionIfNotExist() throws CVSFileNotFoundException {
+
+ if (!exists()) {
+ throw new CVSFileNotFoundException(ioResource + " not found");
+ }
+ }
+
+ /**
+ * Comparing for the work with the sets,
+ *
+ * The coparison is done by the paths.
+ */
+ public int compareTo(Object obj) {
+
+ if (!(obj instanceof CVSResource)) {
+ return -1;
+ } else {
+ return getPath().compareTo(((CVSResource)obj).getPath());
+ }
+ }
+
+ /**
+ * Write String[] to file as lines
+ *
+ * @param file has to be non-null
+ * @param content has to be non-null
+ */
+ protected static void writeToFile(File file, String[] content)
+ throws CVSException {
+ writeToFile(file, content, PLATFORM_NEWLINE);
+ /*
+ BufferedWriter fileWriter;
+
+ try {
+ fileWriter = new BufferedWriter(new FileWriter(file));
+ for (int i = 0; i<content.length; i++) {
+ fileWriter.write(content[i]);
+ fileWriter.newLine();
+ }
+ fileWriter.close();
+ } catch (IOException e) {
+ throw rapIOtoCVS(e);
+ }
+ */
+ }
+
+ /**
+ * Write String[] to file as lines
+ *
+ * @param file has to be non-null
+ * @param content has to be non-null
+ */
+ protected static void writeToFile(File file, String[] content, String delim)
+ throws CVSException {
+
+ BufferedWriter fileWriter;
+
+ deleteIfProtected(file);
+
+ try {
+ fileWriter = new BufferedWriter(new FileWriter(file));
+ for (int i = 0; i<content.length; i++) {
+ fileWriter.write(content[i]);
+ fileWriter.write(delim);
+ }
+ fileWriter.close();
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ }
+
+ /**
+ * Delete the file if it is WriteProtected in order to be able to
+ * write new content in this place
+ */
+ protected static void deleteIfProtected(File ioFile) throws CVSException {
+
+ boolean sucess;
+
+ // If the file is read-only we need to delete it before
+ // we can write it new
+ if (!ioFile.canWrite()) {
+ sucess = ioFile.delete();
+ if (!sucess && ioFile.exists()) {
+ throw new CVSException("Not able to delete file");
+ }
+ }
+ }
+
+ /**
+ * load file in lines to String[]
+ *
+ * @param file has to be non-null and file.exists() == true
+ */
+ protected static String[] readFromFile(File file)
+ throws CVSException {
+
+ BufferedReader fileReader;
+ List fileContentStore = new ArrayList();
+ String line;
+
+ try {
+ fileReader = new BufferedReader(new FileReader(file));
+ while ((line = fileReader.readLine()) != null) {
+ fileContentStore.add(line);
+ }
+ fileReader.close();
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+
+ return (String[]) fileContentStore.toArray(new String[fileContentStore.size()]);
+ }
+
+ /**
+ * This is to be used by the ResourceFactory only
+ */
+ public File getIOResource() {
+ return ioResource;
+ }
+
+ /**
+ * Implement the hashcode on the underlying strings, like it
+ * is done in the equals.
+ */
+ public int hashCode() {
+ return getPath().hashCode();
+ }
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FilePropertiesContainer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FilePropertiesContainer.java
new file mode 100644
index 000000000..a9e4d18ae
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/FilePropertiesContainer.java
@@ -0,0 +1,275 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * A FilePropertiesContainer stores informations about the files
+ * of a folder. It cares about loading saving this information
+ * in the folder the container belongs to.
+ */
+public class FilePropertiesContainer {
+
+ private ICVSFolder cvsFolder;
+
+ private static final String seperator = "/";
+ private static final String FOLDER_ENTRY = "D/";
+ private static final String ENTRIES = FileProperties.ENTRIES;
+
+ /**
+ * Construct a container for the file-infos for
+ * the files of this folder
+ */
+ public FilePropertiesContainer(ICVSFolder cvsFolder, boolean autoSave) {
+ this.cvsFolder = cvsFolder;
+ }
+
+ /**
+ * Costruct a FileProperties-Object for the file.
+ * The file has to be child of the folder.
+ *
+ * Changing the FileProperties does not change anything
+ * in the FilePropertiesContainer. You have to set the
+ * fileInfo to acctually change something.
+ */
+ public FileProperties getFileInfo(String fileName) throws CVSException {
+
+ FileProperties fileProperties = new FileProperties();
+ String key;
+ String property;
+ boolean foundFile = false;
+
+ if (!cvsFolder.isCVSFolder()) {
+ return null;
+ }
+
+ // Go through all the keys that we want to load from
+ // our cvsFolder
+ // take them from cvsFolder and put them into our
+ // new FileProperties
+ for (Iterator i = fileProperties.keySet().iterator(); i.hasNext();) {
+ key = (String) i.next();
+ property = getProperty(key,fileName);
+
+ if (property != null) {
+ fileProperties.putProperty(key,property);
+ foundFile = true;
+ }
+ }
+
+ // If there was nothing to load at all, then we give null back
+ // istead of the empty FileProperties
+ // I do not know if we really need that
+ if (foundFile) {
+ return fileProperties;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the fileinfo into the container.
+ */
+ public void setFileInfo(String fileName, FileProperties fileProperties) throws CVSException {
+
+ Assert.isNotNull(fileName);
+ Assert.isTrue(fileProperties == null || fileName.equals(fileProperties.getName()));
+
+ String key;
+
+ // If we want to "unset" the file, then we just create a
+ // new FileProperties, what is going to have null in all
+ // arguments, and null removes the value.
+ if (fileProperties == null) {
+ fileProperties = new FileProperties();
+ }
+
+ for (Iterator i = fileProperties.keySet().iterator(); i.hasNext();) {
+ key = (String)i.next();
+ putProperty(key,fileName,fileProperties.getProperty(key));
+ }
+ }
+
+ /**
+ * Set a property of a file to the value. Value null removes the
+ * information. Saves this information in the cvsFolder.
+ */
+ private void putProperty(String key,String fileName,String value) throws CVSException {
+
+ String[] data;
+ Map mapData = new HashMap();
+ int start;
+ int end;
+
+ data = cvsFolder.getProperty(key);
+
+ if (data != null) {
+ for (int i = 0; i < data.length; i++) {
+ start = data[i].indexOf(seperator)+1;
+ end = data[i].indexOf(seperator,start+1);
+ mapData.put(data[i].substring(start,end),data[i]);
+ }
+ }
+
+ if (value == null) {
+ mapData.remove(fileName);
+ } else {
+ mapData.put(fileName,formatProperty(fileName,value));
+ }
+
+ cvsFolder.setProperty(key,
+ (String[])mapData.values().toArray(new String[mapData.size()]));
+ }
+
+ /**
+ * Get a property for a file.
+ */
+ private String getProperty(String key, String fileName) throws CVSException {
+
+ String[] data = cvsFolder.getProperty(key);
+ String fileKey = seperator + fileName + seperator;
+
+ if (data == null) {
+ return null;
+ }
+
+ for (int j = 0; j < data.length; j++) {
+ if (data[j].startsWith(fileKey) || data[j].substring(1).startsWith(fileKey)) {
+ return data[j];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Bring a value into a valid property-format. If the
+ * value is not in the "{something}/filename/{something}"-format
+ * "/filename/" is the new prefix of the String.
+ */
+ private String formatProperty(String fileName,String value) {
+
+ int start;
+ int end;
+ String fileKey = seperator + fileName + seperator;
+
+ if (value == null) {
+ return null;
+ }
+
+ start = value.indexOf(seperator)+1;
+ end = value.indexOf(seperator,start+1);
+
+ if (start != -1 && end != -1 &&
+ value.substring(start,end).equals(fileName)) {
+ return value;
+ } else {
+ return fileKey + value;
+ }
+ }
+
+
+ /**
+ * Adds folders to the container
+ *
+ * @throws CVSException if autoSave & !folder.exists()
+ */
+ public void addFolder(String name) throws CVSException {
+ String entryLine;
+ entryLine = FOLDER_ENTRY + name + "////";
+ putProperty(ENTRIES,name,entryLine);
+ }
+
+ /**
+ * Removes folders from the conatainer
+ */
+ public void removeFolder(String name) throws CVSException {
+ putProperty(ENTRIES,name,null);
+ }
+
+ /**
+ * Is the folder in the container ?
+ */
+ public boolean containsFolder(String fileName) throws CVSException {
+ return getProperty(ENTRIES,fileName) != null;
+ }
+
+ /**
+ * This gives a list of all files stated in the
+ * entries. These files do not have to exist on
+ * the filesystem.
+ */
+ public ICVSFile[] getEntriesFileList() throws CVSException {
+
+ List fileList = new ArrayList();
+ String fileName;
+ String[] entries;
+
+ // If we are not in an cvs-folder or the entries are empty we do not have
+ // entry-files.
+ // NOTE: We are setting the entries-variable in that momnet
+ if (!cvsFolder.isCVSFolder() ||
+ (entries = cvsFolder.getProperty(ENTRIES)) == null) {
+ return new ICVSFile[0];
+ }
+
+ for (int i=0; i<entries.length; i++) {
+ if (entries[i].startsWith(seperator)) {
+
+ // get the name of the file with the help of the
+ // of the FileProperties Object. We need to do that in
+ // another way sometime
+ fileName = (new FileProperties(entries[i],null)).getName();
+ fileList.add(cvsFolder.createFile(fileName));
+ }
+ }
+
+ return (ICVSFile[]) fileList.toArray(new ICVSFile[fileList.size()]);
+ }
+
+ /**
+ * This gives a list of all folders stated in the
+ * entries. These files do not have to exist on
+ * the filesystem.
+ */
+ public ICVSFolder[] getEntriesFolderList() throws CVSException {
+
+ List folderList = new ArrayList();
+ String folderName;
+
+ String[] entries;
+
+ // If we are not in an cvs-folder or the entries are empty we do not have
+ // entry-files.
+ // NOTE: We are setting the entries in that momnet
+ if (!cvsFolder.isCVSFolder() ||
+ (entries = cvsFolder.getProperty(ENTRIES)) == null) {
+ return new ICVSFolder[0];
+ }
+
+ for (int i=0; i<entries.length; i++) {
+ if (entries[i].startsWith(FOLDER_ENTRY)) {
+ folderName = entries[i].substring(FOLDER_ENTRY.length());
+ folderName = folderName.substring(0,folderName.indexOf(seperator));
+ folderList.add(cvsFolder.createFolder(folderName));
+ }
+ }
+
+ return (ICVSFolder[]) folderList.toArray(new ICVSFolder[folderList.size()]);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFile.java
new file mode 100644
index 000000000..f04a7289e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFile.java
@@ -0,0 +1,359 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.Calendar;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.util.FileDateFormat;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * Implements the IManagedFile interface on top of an
+ * instance of the ICVSFile interface
+ *
+ * @see IManagedFile
+ */
+class ManagedFile extends ManagedResource implements IManagedFile {
+
+ ICVSFile cvsFile;
+ private static final byte[] BUFFER = new byte[4096];
+
+ /**
+ * Constructor for ManagedFile
+ */
+ ManagedFile(ICVSFile cvsFile) {
+ super();
+ this.cvsFile = cvsFile;
+ }
+
+ /**
+ * @see IManagedFile#getSize()
+ */
+ public long getSize() {
+ return cvsFile.getSize();
+ }
+
+ /**
+ * @see IManagedFile#getFileInfo()
+ */
+ public FileProperties getFileInfo() throws CVSException {
+
+ return getInternalParent().getFileInfo(this);
+
+ }
+
+ /**
+ * @see IManagedFile#setFileInfo(FileProperties)
+ */
+ public void setFileInfo(FileProperties fileInfo) throws CVSException {
+
+ if (!(fileInfo == null || fileInfo.getName().equals(cvsFile.getName()))) {
+ throw new CVSException("Try to set fileInfo where fileInfo.getName() != file.getName()");
+ }
+
+ getInternalParent().setFileInfo(this,fileInfo);
+
+ }
+
+ /**
+ * @see IManagedFile#reciveFrom(OutputStream, IProgressMonitor)
+ */
+ public void receiveFrom(InputStream in,
+ IProgressMonitor monitor,
+ long size,
+ boolean binary,
+ boolean readOnly)
+
+ throws CVSException {
+
+ OutputStream out;
+ String title;
+
+ title = Policy.bind("ManagedFile.receiving",
+ new Object[] {cvsFile.getName()});
+
+ try {
+
+ out = cvsFile.getOutputStream();
+
+ if (binary) {
+ // System.out.println("BinaryReciving: " + getName() + "(" + size + ")");
+ transferWithProgress(in,out,size,monitor,title);
+ } else {
+ // System.out.println("TextReciving: " + getName() + "(" + size + ")");
+ transferText(in,out,size,monitor,title,false);
+ }
+
+ out.close();
+
+ if (readOnly) {
+ cvsFile.setReadOnly();
+ }
+
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ }
+
+ /**
+ * @see IManagedFile#sendTo(InputStream, IProgressMonitor, long)
+ */
+ public void sendTo(
+ OutputStream out,
+ IProgressMonitor monitor,
+ boolean binary)
+ throws CVSException {
+
+ InputStream in;
+ String title;
+ long size = getSize();
+ title = Policy.bind("ManagedFile.sending",
+ new Object[]{cvsFile.getName()});
+
+ try {
+ in = cvsFile.getInputStream();
+
+ if (binary) {
+
+ // Send the size to the server
+ out.write(("" + cvsFile.getSize()).getBytes());
+ out.write(SERVER_NEWLINE.getBytes());
+ transferWithProgress(in,out,size,monitor,title);
+
+ // System.out.println("BinarySending: " + getName() + "(" + size + ")");
+
+ } else {
+
+ // In this case the size has to be computed.
+ // Therefore we do send the size in transferText
+ transferText(in,out,cvsFile.getSize(),monitor,title,true);
+
+ // System.out.println("TextSending: " + getName() + "(" + size + ")");
+ }
+
+ in.close();
+
+ } catch (IOException e) {
+ throw wrapException(e);
+ }
+ }
+ /**
+ * @see IManagedFile#getTimeStamp()
+ */
+ public String getTimeStamp() throws CVSFileNotFoundException {
+
+ exceptionIfNotExists();
+
+ FileDateFormat df = new FileDateFormat();
+
+ return df.formatMill(cvsFile.getTimeStamp());
+ }
+
+ /**
+ * @see IManagedFile#setTimeStamp(long)
+ */
+ public void setTimeStamp(String date) throws CVSException {
+
+ long millSec;
+ Calendar calendar;
+ FileDateFormat df = new FileDateFormat();
+
+ exceptionIfNotExists();
+
+ if (date==null) {
+ // get the current time
+ calendar = Calendar.getInstance();
+ millSec = calendar.getTime().getTime();
+ } else {
+ try {
+ millSec = df.parseMill(date);
+ } catch (ParseException e) {
+ throw new CVSException(0,0,"Format of the Date for a TimeStamp not parseable",e);
+ }
+ }
+
+ cvsFile.setTimeStamp(millSec);
+ }
+
+ /**
+ * @see IManagedResource#isFolder()
+ */
+ public boolean isFolder() {
+ return false;
+ }
+
+ /**
+ * @see IManagedResource#isManaged()
+ */
+ public boolean isManaged() throws CVSException {
+ return (getInternalParent().getFileInfo(this) != null);
+ }
+
+ /**
+ * Send/Recive a textFile from/to the server. It does the conversion
+ * of the newlines and sends the filesize to the server (only on a
+ * send)
+ */
+ protected static void transferText(InputStream in,
+ OutputStream out,
+ long size,
+ IProgressMonitor monitor,
+ String title,
+ boolean toServer)
+ throws IOException {
+
+ // If we get a file bigger than 2 GigaByte, this does not
+ // work
+ Assert.isTrue(size < Integer.MAX_VALUE);
+
+ if (size > 25000) {
+
+ monitor.setTaskName(
+ Policy.bind(
+ "ManagedFile.transfer",
+ new Object[]{title,new Long(0),new Long(size/1024)}
+ )
+ );
+
+ }
+
+ byte[] buffer = new byte[(int)size];
+
+ // Get the content from the file
+ int num = in.read(buffer);
+ int pos = num;
+ while ((num != -1) && (size - pos > 0)) {
+ Policy.checkCanceled(monitor);
+ num = in.read(buffer, pos, ((int)size) - pos);
+ pos += num;
+ }
+
+ // care about newlines
+ if (toServer) {
+ buffer = Util.replace(buffer,PLATFORM_NEWBYTE,SERVER_NEWBYTE);
+ // Send the size to the server
+ out.write(("" + buffer.length).getBytes());
+ out.write(SERVER_NEWLINE.getBytes());
+
+ } else {
+ buffer = Util.replace(buffer,PLATFORM_NEWBYTE,SERVER_NEWBYTE);
+ buffer = Util.replace(buffer,SERVER_NEWBYTE,PLATFORM_NEWBYTE);
+ }
+
+ out.write(buffer);
+ }
+
+ /**
+ * Transfer an InputStream to an OutputStream
+ * and update the monitor in between.
+ *
+ * Used for saving files from server
+ * on disc, etc.
+ */
+ protected static void transferWithProgress(
+ InputStream in,
+ OutputStream out,
+ long size,
+ IProgressMonitor monitor,
+ String title)
+ throws IOException {
+
+ // This special transfer utility will show progress to
+ // the monitor for files that are bigger than 25K
+ boolean progress = size > 25000;
+ int read = 0;
+ long totalRead = 0;
+ long ksize = size / 1024;
+ // buffer size is smaller than MAXINT...
+ int toRead = (int) Math.min(BUFFER.length, size);
+ synchronized (BUFFER) {
+ while ((totalRead < size) && (read = in.read(BUFFER, 0, toRead)) != -1) {
+ if (progress && totalRead > 0) {
+ monitor.subTask(
+ Policy.bind(
+ "ManagedFile.transfer",
+ new Object[] { title, new Long(totalRead / 1024), new Long(ksize)}));
+ monitor.worked(read);
+ }
+ totalRead += read;
+ out.write(BUFFER, 0, read);
+ toRead = (int) Math.min(BUFFER.length, size - totalRead);
+ }
+ }
+ }
+
+ /**
+ * @see ManagedResource#getResource()
+ */
+ public ICVSResource getCVSResource() {
+ return cvsFile;
+ }
+
+ /**
+ * @see IManagedFile#isDirty()
+ */
+ public boolean isDirty() throws CVSException {
+
+ if (!exists() || !isManaged()) {
+ return true;
+ }
+
+ return !getTimeStamp().equals(getFileInfo().getTimeStamp());
+ }
+
+ /**
+ * @see IManagedResource#accept(IManagedVisitor)
+ */
+ public void accept(IManagedVisitor visitor) throws CVSException {
+ visitor.visitFile(this);
+ }
+
+ /**
+ * @see IManagedFile#moveTo(IManagedFile)
+ */
+ public void moveTo(IManagedFile mFile) throws CVSException, ClassCastException {
+ cvsFile.moveTo(((ManagedFile)mFile).cvsFile);
+ }
+
+ /**
+ * @see IManagedFile#getContent()
+ */
+ public String[] getContent() throws CVSException {
+ return cvsFile.getContent();
+ }
+
+ /**
+ * @see IManagedResource#getRemoteLocation()
+ */
+ public String getRemoteLocation(IManagedFolder stopSearching) throws CVSException {
+ return getParent().getRemoteLocation(stopSearching) + separator + getName();
+ }
+
+ /**
+ * @see IManagedResource#unmanage()
+ */
+ public void unmanage() throws CVSException {
+ setFileInfo(null);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFolder.java
new file mode 100644
index 000000000..5c8936d82
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedFolder.java
@@ -0,0 +1,603 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.commands.FileNameMatcher;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * Implements the IManagedFolder interface on top of an
+ * instance of the ICVSFolder interface
+ *
+ * @see IManagedFolder
+ */
+class ManagedFolder extends ManagedResource implements IManagedFolder {
+
+ public static final String PWD_PROPERTY = "Password";
+
+ private ICVSFolder cvsFolder;
+ private FilePropertiesContainer fileInfoContainer;
+
+ /**
+ * Constructor for ManagedFolder
+ */
+ ManagedFolder(ICVSFolder cvsFolder) {
+ super();
+ this.cvsFolder = cvsFolder;
+ fileInfoContainer = new FilePropertiesContainer(cvsFolder,AUTO_SAVE);
+ }
+
+ /**
+ * @see IManagedFolder#getFolders()
+ */
+ public IManagedFolder[] getFolders() throws CVSException {
+
+ ICVSFolder[] entrieFolders;
+ ICVSFolder[] underlyingFolders;
+ ICVSFolder[] allFolders;
+ IManagedFolder[] resultFolders;
+
+ exceptionIfNotExists();
+
+ entrieFolders = fileInfoContainer.getEntriesFolderList();
+ underlyingFolders = cvsFolder.getFolders();
+
+ // merge the list of the folders
+ allFolders = (ICVSFolder[]) merge(entrieFolders,
+ underlyingFolders,
+ new ICVSFolder[0]);
+
+ // NIK: we could add a folder, that we are going to ignore
+ // afterwards, it would stay forever in the entries
+ // as to be added ?!?
+ allFolders = (ICVSFolder[])removeIgnored(allFolders, new ICVSFolder[0]);
+
+ // wrap the cvsFolders to managedFolders
+ resultFolders = new IManagedFolder[allFolders.length];
+ for (int i=0; i<allFolders.length; i++) {
+ resultFolders[i] = createResourceFrom(allFolders[i]);
+ }
+
+ return resultFolders;
+ }
+
+ /**
+ * Takes two Array and returns the contend of the two arrays
+ * minus all dublication entries.
+ * A dublication is a entrie x that has an entry y allready in the
+ * result with x.equals(y).
+ *
+ * e.g.: tmp = (String[])merge(new String[]{"a","b","c"},
+ * new String[]{"b","c","d"},
+ * new String[0]);
+ *
+ * result: tmp.equals(new String[]{"a","b","c","d"})
+ */
+ private Object[] merge(Object[] array1, Object[] array2, Object[] resultArray) {
+
+ Set mergeSet = new TreeSet();
+
+ for (int i=0; i<array1.length; i++) {
+ mergeSet.add(array1[i]);
+ }
+
+ for (int i=0; i<array2.length; i++) {
+ mergeSet.add(array2[i]);
+ }
+
+ return mergeSet.toArray(resultArray);
+ }
+
+ /**
+ * @see IManagedFolder#getFiles()
+ */
+ public IManagedFile[] getFiles() throws CVSException {
+
+ ICVSFile[] entrieFiles;
+ ICVSFile[] underlyingFiles;
+ ICVSFile[] allFiles;
+ IManagedFile[] resultFiles;
+
+ exceptionIfNotExists();
+
+ entrieFiles = fileInfoContainer.getEntriesFileList();
+ underlyingFiles = cvsFolder.getFiles();
+
+ // merge the list of the Files
+ allFiles = (ICVSFile[]) merge(entrieFiles,
+ underlyingFiles,
+ new ICVSFile[0]);
+ allFiles = (ICVSFile[])removeIgnored(allFiles, new ICVSFile[0]);
+
+
+ // wrap the cvsFiles to managedFiles
+ resultFiles = new IManagedFile[allFiles.length];
+ for (int i=0; i<allFiles.length; i++) {
+ resultFiles[i] = createResourceFrom(allFiles[i]);
+ }
+
+ return resultFiles;
+ }
+
+ /**
+ * @see IManagedFolder#createFolder(String)
+ */
+ public IManagedFolder getFolder(String name) throws CVSException {
+ return createResourceFrom(cvsFolder.createFolder(name));
+ }
+
+ /**
+ * @see IManagedFolder#createFile(String)
+ */
+ public IManagedFile getFile(String name) throws CVSException {
+ return createResourceFrom(cvsFolder.createFile(name));
+ }
+
+ /**
+ * @see IManagedFolder#childExists(String)
+ */
+ public boolean childExists(String path) {
+ return cvsFolder.childExists(path);
+ }
+
+ /**
+ * @see IManagedFolder#getChild(String)
+ */
+ public IManagedResource getChild(String name) throws CVSException {
+
+ IManagedResource mResource;
+
+ mResource = getRealChild(name);
+
+ if (mResource == null) {
+ mResource = getVirtualChild(name);
+ }
+
+ if (mResource == null) {
+ throw new CVSFileNotFoundException("used getChild(" + name + ") on a not existing file");
+ }
+
+ return mResource;
+
+ }
+
+ /**
+ * Tries to find the child "path" in the file-system. If the child is not
+ * there, then it returns null
+ */
+ private IManagedResource getRealChild(String name) throws CVSException {
+
+ ICVSResource cvsResource;
+
+ if (!cvsFolder.childExists(name)) {
+ return null;
+ }
+
+ cvsResource = cvsFolder.getChild(name);
+
+ if (cvsResource.isFolder()) {
+ return createResourceFrom((ICVSFolder)cvsResource);
+ } else {
+ return createResourceFrom((ICVSFile)cvsResource);
+ }
+ }
+
+ /**
+ * Tries to find the child "path" in the entries. If it is not there the
+ * method returns null
+ */
+ private IManagedResource getVirtualChild(String name) throws CVSException {
+
+ IManagedFolder virtualParent;
+
+ IManagedFolder[] folders;
+ IManagedFile[] files;
+
+ // get the direct parent of the child that we want to
+ // find if it does not exist then the virtual child
+ // can not exist
+ // We "cheat" and say, that the virtualChild is going to
+ // be a file. But as we use it for string-manipulation
+ // only this is allright
+ virtualParent = getFile(name).getParent();
+ name = getFile(name).getName();
+
+ if (!virtualParent.exists()) {
+ return null;
+ }
+
+ folders = virtualParent.getFolders();
+
+ for (int i=0; i<folders.length; i++) {
+ if (folders[i].getName().equals(name)) {
+ return folders[i];
+ }
+ }
+
+ files = virtualParent.getFiles();
+
+ for (int i=0; i<files.length; i++) {
+ if (files[i].getName().equals(name)) {
+ return files[i];
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * @see IManagedFolder#mkdir()
+ */
+ public void mkdir() throws CVSException {
+ cvsFolder.mkdir();
+ }
+
+ /**
+ * @see IManagedFolder#flush(boolean)
+ */
+ public void flush(boolean deep) {
+ // Does do nothing as AUTO_SAVE == true
+ //
+ // Otherwise we need somthing like load and save.
+ }
+
+ /**
+ * @see IManagedFolder#getFolderInfo()
+ */
+ public FolderProperties getFolderInfo() throws CVSException {
+
+ FolderProperties folderProperties = new FolderProperties();
+ String key;
+ String[] data;
+
+ if (!exists() || !cvsFolder.isCVSFolder()) {
+ return null;
+ }
+
+ for (Iterator i = folderProperties.keySet().iterator(); i.hasNext();) {
+ key = (String) i.next();
+ data = cvsFolder.getProperty(key);
+
+ if (data == null) {
+ // throw new CVSException("The FolderInformation in the folder " + cvsFolder + " is partrtial");
+ continue;
+ } else {
+ folderProperties.putProperty(key,data[0]);
+ }
+ }
+
+ return folderProperties;
+ }
+
+ /**
+ * @see IManagedFolder#setFolderInfo(FolderProperties)
+ */
+ public void setFolderInfo(FolderProperties folderProperties) throws CVSException {
+
+ String key;
+
+ if (folderProperties == null) {
+ removeFolderInfo();
+ return;
+ }
+
+ exceptionIfNotExists();
+ cvsFolder.makeCVSFolder();
+
+ for (Iterator i = folderProperties.keySet().iterator(); i.hasNext();) {
+ key = (String) i.next();
+ if (folderProperties.getProperty(key) == null) {
+ cvsFolder.setProperty(key,null);
+ } else {
+ cvsFolder.setProperty(key,
+ new String[]{folderProperties.getProperty(key)});
+ }
+ }
+
+ getInternalParent().addFolderEntrie(this);
+
+ }
+
+ /**
+ * Remove the FileProperties and therefore all the knowlege of the entrie-systen
+ * about this folder.
+ * The properties are deleted and the entries of the parent-folder
+ * are updated.
+ */
+ private void removeFolderInfo() throws CVSException {
+
+ if (exists() && cvsFolder.isCVSFolder()) {
+ cvsFolder.unmakeCVSFolder();
+ }
+
+ getInternalParent().removeFolderEntrie(this);
+ }
+
+ /**
+ * @see IManagedFolder#setProperty(String, String[])
+ */
+ public void setProperty(String key, String[] content) throws CVSException {
+
+ // We want to create a cvs-folder on the first time
+ // setting a property
+ cvsFolder.makeCVSFolder();
+
+ cvsFolder.setProperty(key,content);
+
+ }
+
+ /**
+ * @see IManagedFolder#unsetProperty(String)
+ */
+ public void unsetProperty(String key) throws CVSException {
+
+ // Otherwise we do not want to do anything
+ if (cvsFolder.isCVSFolder()) {
+ cvsFolder.setProperty(key,null);
+ }
+ }
+
+ /**
+ * @see IManagedFolder#getProperty(String)
+ */
+ public String[] getProperty(String key) throws CVSException {
+ return cvsFolder.getProperty(key);
+ }
+
+ /**
+ * @see IManagedResource#isFolder()
+ */
+ public boolean isFolder() {
+ return true;
+ }
+
+ protected boolean isIgnored(String child) throws CVSException {
+ // NOTE: This is the wrong place for this
+ if (child.equals("CVS"))
+ return true;
+ FileNameMatcher matcher = null;
+ try {
+ matcher = FileNameMatcher.getIgnoreMatcherFor(cvsFolder);
+ } catch (IOException e) {
+ // Log the exception and return files unchanged
+ throw wrapException(e);
+ }
+ if (matcher == null)
+ return false;
+ return matcher.match(child);
+ }
+
+ /**
+ * Set the entry and the rest of the fileInfo for the file.
+ */
+ void setFileInfo(IManagedFile file, FileProperties fileInfo) throws CVSException {
+
+ Assert.isTrue(file.getParent().equals(this));
+ Assert.isTrue(fileInfo == null || file.getName().equals(fileInfo.getName()));
+
+ exceptionIfNotExists();
+ cvsFolder.makeCVSFolder();
+
+ fileInfoContainer.setFileInfo(file.getName(),fileInfo);
+ }
+
+ /**
+ * Get the fileInfo for a specific file.
+ *
+ * @param file has to satisfy file.getParent().equals(this)
+ * @return null if isManaged() = false
+ */
+ FileProperties getFileInfo(IManagedFile file) throws CVSException {
+
+ Assert.isTrue(file.getParent().equals(this));
+
+ return fileInfoContainer.getFileInfo(file.getName());
+ }
+
+// /**
+// * Remove the fileInfo for a specific file. If it was not there
+// * before nothing happens.
+// *
+// * @param file has to satisfy file.getParent().equals(this)
+// */
+// void removeFileInfo(IManagedFile file) throws CVSException {
+//
+// Assert.isTrue(file.getParent().equals(this));
+//
+// fileInfoContainer.removeFileInfo(file.getName());
+// }
+
+ /**
+ * Add an folder to the entries of this folder. If the folder
+ * was in the list allready, it just stays there.
+ *
+ * @param folder must satisfy: folder.getParent().equals(this)
+ * @throws CVSException if AUTO_SAVE & !folder.exists()
+ */
+ void addFolderEntrie(IManagedFolder folder) throws CVSException {
+
+ Assert.isTrue(folder.getParent().equals(this));
+
+ // This could be the project-folder wich is not a CVSFolder
+ // and we do not need to updated the entries
+ if (cvsFolder.isCVSFolder()) {
+ fileInfoContainer.addFolder(folder.getName());
+ }
+ }
+
+ /**
+ * Remove an folder from the entrie of this folder. If the folder
+ * has not been there before it is not in it after the operation.
+ *
+ * @param folder must satisfy: folder.getParent().equals(this)
+ * @throws CVSException if AUTO_SAVE & !folder.exists()
+ */
+ void removeFolderEntrie(IManagedFolder folder) throws CVSException {
+
+ Assert.isTrue(folder.getParent().equals(this));
+
+ // This could be the project-folder wich is not a CVSFolder
+ // and we do not need to updated the entries
+ if (cvsFolder.isCVSFolder()) {
+ fileInfoContainer.removeFolder(folder.getName());
+ }
+ }
+
+ /**
+ * Is the folder in the entries
+ *
+ * @return false if !folder.getParent().equals(this)
+ * @throws CVSException if AUTO_SAVE & !folder.exists()
+ */
+ boolean containsFolderEntrie(IManagedFolder folder) throws CVSException {
+
+ if (!folder.getParent().equals(this) || !cvsFolder.isCVSFolder()) {
+ return false;
+ } else {
+ return fileInfoContainer.containsFolder(folder.getName());
+ }
+ }
+
+ /**
+ * @see IManagedResource#isManaged()
+ */
+ public boolean isManaged() throws CVSException {
+
+ // To be implemented after ManagedFolder
+ // we need a method, that tells us wether a folder
+ // or a file is in the entries-property.
+
+ return getInternalParent().containsFolderEntrie(this);
+ }
+
+ /**
+ * @see ManagedResource#getResource()
+ */
+ public ICVSResource getCVSResource() {
+ return cvsFolder;
+ }
+
+
+ /**
+ * @see IManagedFolder#isCVSFolder()
+ */
+ public boolean isCVSFolder() throws CVSException {
+ return exists() && cvsFolder.isCVSFolder();
+ }
+
+
+ /**
+ * @see IManagedFolder#acceptChildren(IManagedVisitor)
+ */
+ public void acceptChildren(IManagedVisitor visitor) throws CVSException {
+
+ IManagedResource[] subFiles;
+ IManagedResource[] subFolders;
+
+ subFiles = getFiles();
+ subFolders = getFolders();
+
+ for (int i=0; i<subFiles.length; i++) {
+ subFiles[i].accept(visitor);
+ }
+
+ for (int i=0; i<subFolders.length; i++) {
+ subFolders[i].accept(visitor);
+ }
+ }
+
+ /**
+ * @see IManagedResource#accept(IManagedVisitor)
+ */
+ public void accept(IManagedVisitor visitor) throws CVSException {
+ visitor.visitFolder(this);
+ }
+
+ /**
+ * @see IManagedResource#getRemoteLocation(IManagedFolder)
+ */
+ public String getRemoteLocation(IManagedFolder stopSearching) throws CVSException {
+
+ String parentLocation;
+
+ if (getFolderInfo() != null) {
+ return getFolderInfo().getRemoteLocation();
+ }
+
+ if (equals(stopSearching)) {
+ return null;
+ }
+
+ parentLocation = getParent().getRemoteLocation(stopSearching);
+ if (parentLocation == null) {
+ return null;
+ } else {
+ return parentLocation + separator + getName();
+ }
+
+ }
+
+ /**
+ * @see IManagedResource#unmanage()
+ * @deprecated uses unmakeCVSFolder intstead of setFolderInfo(null)
+ */
+ public void unmanage() throws CVSException {
+ accept(new IManagedVisitor() {
+ public void visitFile(IManagedFile file) throws CVSException {}
+ public void visitFolder(IManagedFolder folder) throws CVSException {
+ folder.acceptChildren(this);
+ folder.setFolderInfo(null);
+ }
+ });
+ }
+
+ /**
+ * Remove the ignored resources from the provided list of resources.
+ * The type variable is used to determine the type of the elements in
+ * the resulting array.
+ */
+ private ICVSResource[] removeIgnored(ICVSResource[] resources, ICVSResource[] type) throws CVSException {
+ FileNameMatcher matcher = null;
+ try {
+ matcher = FileNameMatcher.getIgnoreMatcherFor(cvsFolder);
+ } catch (IOException e) {
+ // Log the exception and return files unchanged
+ throw wrapException(e);
+ }
+ if (matcher == null)
+ return resources;
+ List result = new ArrayList(resources.length);
+ for (int i=0;i<resources.length;i++) {
+ if (!matcher.match(resources[i].getName()))
+ result.add(resources[i]);
+ }
+ if (result.size() == resources.length)
+ return resources;
+ return (ICVSResource[])result.toArray(type);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedResource.java
new file mode 100644
index 000000000..738ec7ec1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ManagedResource.java
@@ -0,0 +1,258 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.CVSFileException;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * Implements the IManagedResource interface on top of an
+ * instance of the ICVSResource interface
+ *
+ * @see IManagedResource
+ */
+abstract class ManagedResource implements IManagedResource {
+
+ static final String PLATFORM_NEWLINE = System.getProperty("line.separator");
+ static final String SERVER_NEWLINE = "\n";
+
+ static final byte[] PLATFORM_NEWBYTE = PLATFORM_NEWLINE.getBytes();
+ static final byte[] SERVER_NEWBYTE = SERVER_NEWLINE.getBytes();
+
+ /**
+ * Constructor for ManagedResource
+ */
+ ManagedResource() {
+ }
+
+ /**
+ * Get the extention of the path of resource
+ * relative to the path of root
+ *
+ * @throws CVSException if root is not a root-folder of resource
+ */
+ public String getRelativePath(IManagedFolder root)
+ throws CVSException {
+
+ ManagedResource rootFolder;
+
+ try {
+ rootFolder = (ManagedResource)root;
+ } catch (ClassCastException e) {
+ throw new CVSException(0,0,"two different implementations of IManagedResource used",e);
+ }
+
+ return getRelativePath(getCVSResource().getPath(), rootFolder.getCVSResource().getPath());
+
+ }
+
+ /**
+ * Get the extention of the path of resource
+ * relative to the path of root
+ *
+ * seperator is the seperation between the different folders
+ *
+ * @throws CVSException if root is not a root-folder of resource
+ */
+ public static String getRelativePath(String resourceName, String rootName)
+ throws CVSException {
+
+ String relativePath;
+
+ if (!resourceName.startsWith(rootName)) {
+ throw new CVSException("Internal error, resource does not start with root.");
+ }
+
+ // Otherwise we would get an ArrayOutOfBoundException
+ // in case of two equal Resources
+ if (rootName.length() == resourceName.length()) {
+ return "";
+ }
+
+ // Get rid of the seperator, that would be in the
+ // beginning, if we did not go from +1
+ relativePath = resourceName.substring(rootName.length() + 1);
+ return convertSeparatorOutgoing(relativePath);
+
+ }
+
+ /**
+ * @see IManagedResource#delete()
+ */
+ public void delete() {
+ getCVSResource().delete();
+ }
+
+ /**
+ * @see IManagedResource#exists()
+ */
+ public boolean exists() {
+ return getCVSResource().exists();
+ }
+
+ /**
+ * @see IManagedResource#getParent()
+ */
+ public IManagedFolder getParent() {
+ return new ManagedFolder(getCVSResource().getParent());
+ }
+
+ /**
+ * @see IManagedResource#getParent()
+ */
+ ManagedFolder getInternalParent() {
+ return new ManagedFolder(getCVSResource().getParent());
+ }
+
+
+ /**
+ * @see IManagedResource#getName()
+ */
+ public String getName() {
+ return getCVSResource().getName();
+ }
+
+ /**
+ * @see IManagedResource#isIgnored()
+ */
+ public boolean isIgnored() throws CVSException {
+ return (!isManaged() && ((ManagedFolder)getParent()).isIgnored(getName()));
+ }
+
+ /**
+ * Create a IManagedFolder from a CVSFolder
+ */
+ public static IManagedFolder createResourceFrom(ICVSFolder folder) {
+ return new ManagedFolder(folder);
+ }
+
+ /**
+ * Create a IManagedFile form a CVSFile
+ *
+ * For internal use only
+ */
+ public static IManagedFile createResourceFrom(ICVSFile file) {
+ return new ManagedFile(file);
+ }
+
+ /**
+ * Clean up incoming path
+ * replaces "/" and "\\" for ICVSResource.seperator
+ */
+ static String convertSeparatorIncoming(String path) {
+ return convertSeperator(path, ICVSResource.seperator);
+ }
+
+ /**
+ * Clean up outgoing path
+ * replaces "/" and "\\" for this.seperator
+ */
+ static String convertSeparatorOutgoing(String path) {
+ return convertSeperator(path, separator);
+ }
+
+ /**
+ * replaces "/" and "\\" for newSeperator
+ * @param newSeperator has to be "/" or "\\"
+ */
+ private static String convertSeperator(String path,String newSeperator) {
+
+ Assert.isTrue(newSeperator.equals("/") || newSeperator.equals("\\"));
+
+ if (newSeperator.equals("/")) {
+ return path.replace('\\','/');
+ } else {
+ return path.replace('/','\\');
+ }
+ }
+
+ /**
+ * Throws an CVSFileNotFoundException if exists() = false
+ */
+ void exceptionIfNotExists() throws CVSFileNotFoundException {
+ if (!exists()) {
+ throw new CVSFileNotFoundException(getName() + " does not exist");
+ }
+ }
+
+ /**
+ * Two ManagedResources are equal, if there cvsResources are
+ * equal (and that is, if the point to the same file)
+ */
+ public boolean equals(Object obj) {
+
+ if (!(obj instanceof ManagedResource)) {
+ return false;
+ } else {
+ return getCVSResource().equals(((ManagedResource) obj).getCVSResource());
+ }
+ }
+
+ /**
+ * Comparing for the work with the sets,
+ *
+ * The coparison is done by the underlying cvsResources.
+ */
+ public int compareTo(Object obj) {
+ if (!(obj instanceof ManagedResource)) {
+ return -1;
+ } else {
+ return getCVSResource().compareTo(((ManagedResource) obj).getCVSResource());
+ }
+ }
+
+ /**
+ * Generate a Standard CVSException for an
+ * IOException
+ *
+ * Copied from CVSResource, we might have other texts here
+ */
+ protected static CVSException wrapException(IOException e) {
+ return new CVSException(IStatus.ERROR,
+ CVSException.IO_FAILED,
+ "An IOException occured while using your file-system.",
+ e);
+ }
+
+ /**
+ * In order not to intrudce an new resource variable in this class we
+ * have an abstract method that returns the resource.
+ *
+ * As the resource could be of two different types, we do not want to
+ * save it here and do the cast in the Folder or the File
+ */
+ public abstract ICVSResource getCVSResource();
+
+ /**
+ * Implement the hashcode on the underlying strings, like it
+ * is done in the equals.
+ */
+ public int hashCode() {
+ return getCVSResource().hashCode();
+ }
+ /**
+ * Comparing for the work with the sets,
+ *
+ * The coparison is done by the paths.
+ *
+ public int compareTo(Object obj) {
+ return cvsResource.compareTo(obj);
+ }
+ */
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
new file mode 100644
index 000000000..8241ed0da
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
@@ -0,0 +1,122 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.response.IResponseHandler;
+import org.eclipse.team.internal.ccvs.core.response.custom.IStatusListener;
+import org.eclipse.team.internal.ccvs.core.response.custom.LogHandler;
+import org.eclipse.team.internal.ccvs.core.response.custom.StatusMessageHandler;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.ILogEntry;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+
+/**
+ * This class provides the implementation of IRemoteFile
+ */
+public class RemoteFile extends RemoteResource implements IRemoteFile {
+
+ /**
+ * Constructor for RemoteFile.
+ */
+ protected RemoteFile(RemoteFolder parent, String name, String tag) {
+ super(parent, name, tag);
+ }
+
+ /**
+ * @see IRemoteFile#getContents()
+ */
+ public InputStream getContents(final IProgressMonitor monitor) throws TeamException {
+
+ // Perform a "cvs update..."
+ RemoteManagedFolder folder = new RemoteManagedFolder(".", getConnection(), parent.getFullPath(), getName());
+ List localOptions = getLocalOptionsForTag();
+ Client.execute(
+ Client.UPDATE,
+ Client.EMPTY_ARGS_LIST,
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{getName()},
+ folder,
+ monitor,
+ CVSTeamProvider.getPrintStream(),
+ getConnection(),
+ null);
+ return ((RemoteManagedFile)folder.getChild(getName())).getCachedContents();
+ }
+
+ /**
+ * @see IRemoteFile#getLogEntries()
+ */
+ public ILogEntry[] getLogEntries(IProgressMonitor monitor) throws CVSException {
+
+ // NOTE: Should we be using the localOptions here?
+
+ // Perform a "cvs status..." with a custom message hanlder
+ RemoteManagedFolder folder = new RemoteManagedFolder(".", getConnection(), parent.getFullPath(), getName());
+ List localOptions = getLocalOptionsForTag();
+ List entries = new ArrayList();
+ Client.execute(
+ Client.STATUS,
+ Client.EMPTY_ARGS_LIST,
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{getName()},
+ folder,
+ monitor,
+ CVSTeamProvider.getPrintStream(),
+ getConnection(),
+ new IResponseHandler[] {new LogHandler(this, entries)});
+ return (ILogEntry[])entries.toArray(new ILogEntry[entries.size()]);
+ }
+
+ /**
+ * @see IRemoteFile#getRevision()
+ */
+ public String getRevision(IProgressMonitor monitor) throws CVSException {
+
+ // Create a listener for receiving the revision info
+ final String[] revision = new String[] { null };
+ IStatusListener listener = new IStatusListener() {
+ public void fileStatus(IPath path, String remoteRevision) {
+ revision[0] = remoteRevision;
+ }
+ };
+
+ // Perform a "cvs status..." with a custom message hanlder
+ RemoteManagedFolder folder = new RemoteManagedFolder(".", getConnection(), parent.getFullPath(), getName());
+ List localOptions = getLocalOptionsForTag();
+ Client.execute(
+ Client.STATUS,
+ Client.EMPTY_ARGS_LIST,
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{getName()},
+ folder,
+ monitor,
+ CVSTeamProvider.getPrintStream(),
+ getConnection(),
+ new IResponseHandler[] {new StatusMessageHandler(listener)});
+ return revision[0];
+ }
+
+ /**
+ * @see IRemoteResource#getType()
+ */
+ public int getType() {
+ return FILE;
+ }
+
+ public RemoteFileRevision toRemoteFileRevision(String revision) {
+ return new RemoteFileRevision(parent, getName(), revision);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFileRevision.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFileRevision.java
new file mode 100644
index 000000000..21ea6f6e2
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFileRevision.java
@@ -0,0 +1,38 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+ /**
+ * Same as a RemoteFile except that the tag is fixed to a particular revision
+ */
+public class RemoteFileRevision extends RemoteFile {
+
+ /**
+ * Constructor for RemoteFileRevision.
+ * @param parent
+ * @param name
+ * @param tag
+ */
+ protected RemoteFileRevision(RemoteFolder parent, String name, String tag) {
+ super(parent, name, tag);
+ }
+
+ /**
+ * @see IRemoteFile#getRevision()
+ */
+ public String getRevision(IProgressMonitor monitor) throws CVSException {
+ return tag;
+ }
+
+ public String getRevision() {
+ return tag;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolder.java
new file mode 100644
index 000000000..387c79065
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolder.java
@@ -0,0 +1,113 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.response.IResponseHandler;
+import org.eclipse.team.internal.ccvs.core.response.custom.IUpdateMessageListener;
+import org.eclipse.team.internal.ccvs.core.response.custom.UpdateErrorHandler;
+import org.eclipse.team.internal.ccvs.core.response.custom.UpdateMessageHandler;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.ccvs.core.IRemoteResource;
+
+/**
+ * This class provides the implementation of IRemoteFolder
+ */
+public class RemoteFolder extends RemoteResource implements IRemoteFolder {
+
+ /**
+ * Constructor for RemoteFolder.
+ */
+ protected RemoteFolder(RemoteFolder parent, String name, String tag) {
+ super(parent, name, tag);
+ }
+
+ /**
+ * @see IRemoteFolder#getMembers()
+ */
+ public IRemoteResource[] getMembers(IProgressMonitor monitor) throws TeamException {
+ return getMembers(tag, monitor);
+ }
+
+ /**
+ * @see IRemoteRoot#getMembers()
+ */
+ public IRemoteResource[] getMembers(final String tagName, final IProgressMonitor monitor) throws TeamException {
+
+ // Create the listener for remote files and folders
+ final List errors = new ArrayList();
+ final List newRemoteDirectories = new ArrayList();
+ final List newRemoteFiles = new ArrayList();
+ IUpdateMessageListener listener = new IUpdateMessageListener() {
+ public void directoryInformation(IPath path, boolean newDirectory) {
+ if (newDirectory && path.segmentCount() == 1)
+ newRemoteDirectories.add(path.lastSegment());
+// monitor.subTask(path.lastSegment().toString());
+// monitor.worked(1);
+ }
+ public void directoryDoesNotExist(IPath path) {
+// monitor.worked(1);
+ }
+ public void fileInformation(char type, String filename) {
+ IPath filePath = new Path(filename);
+ if( filePath.segmentCount() == 1 ) {
+ String properFilename = filePath.lastSegment();
+ newRemoteFiles.add(properFilename);
+// monitor.subTask(properFilename);
+// monitor.worked(1);
+ }
+ }
+ };
+
+ // Build the local options
+ List localOptions = new ArrayList();
+ localOptions.add("-d");
+ if ((tagName != null) && (!tagName.equals("HEAD"))) {
+ localOptions.add(Client.TAG_OPTION);
+ localOptions.add(tagName);
+ }
+
+ // Perform a "cvs -n update -d -r tagName folderName" with custom message and error handlers
+ try {
+ Client.execute(
+ Client.UPDATE,
+ new String[]{Client.NOCHANGE_OPTION},
+ (String[])localOptions.toArray(new String[localOptions.size()]),
+ new String[]{"."},
+ new RemoteManagedFolder(".", getConnection(), getFullPath()),
+ monitor,
+ CVSTeamProvider.getPrintStream(),
+ getConnection(),
+ new IResponseHandler[]{new UpdateMessageHandler(listener), new UpdateErrorHandler(listener, errors)});
+ } catch (CVSException e) {
+ throw CVSTeamProvider.wrapException(e, errors);
+ }
+ List result = new ArrayList();
+ for (int i=0;i<newRemoteDirectories.size();i++)
+ result.add(new RemoteFolder(this, (String)newRemoteDirectories.get(i), tagName));
+ for (int i=0;i<newRemoteFiles.size();i++)
+ result.add(new RemoteFile(this, (String)newRemoteFiles.get(i), tagName));
+ return (IRemoteResource[])result.toArray(new IRemoteResource[0]);
+ }
+
+ /**
+ * @see IRemoteResource#getType()
+ */
+ public int getType() {
+ return FOLDER;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFile.java
new file mode 100644
index 000000000..9b8dae480
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFile.java
@@ -0,0 +1,148 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+
+/**
+ * This class mimics an IManagedFile in order to retrieve the contents of a file
+ * from the server without storing it in the local file system. It is used along with
+ * RemoteManagedFolder by RemoteFile.
+ */
+public class RemoteManagedFile extends RemoteManagedResource implements IManagedFile {
+
+ private FileProperties info;
+ private ByteArrayOutputStream bos;
+
+ public RemoteManagedFile(String name, IManagedFolder parent, ICVSRepositoryLocation repository) {
+ super(name, parent, repository);
+ }
+
+ /**
+ * @see IManagedFile#getSize()
+ */
+ public long getSize() {
+ return 0;
+ }
+
+ /**
+ * @see IManagedFile#getFileInfo()
+ */
+ public FileProperties getFileInfo() throws CVSException {
+ return info;
+ }
+
+ /**
+ * @see IManagedFile#setFileInfo(FileProperties)
+ */
+ public void setFileInfo(FileProperties fileInfo) throws CVSException {
+ info = fileInfo;
+ }
+
+ /**
+ * @see IManagedFile#sendTo(OutputStream, IProgressMonitor, boolean)
+ */
+ public void sendTo(
+ OutputStream outputStream,
+ IProgressMonitor monitor,
+ boolean binary)
+ throws CVSException {
+
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedFile#receiveFrom(InputStream, IProgressMonitor, long, boolean)
+ */
+ public void receiveFrom(
+ InputStream inputStream,
+ IProgressMonitor monitor,
+ long size,
+ boolean binary,
+ boolean readOnly)
+ throws CVSException {
+
+ try {
+ bos = new ByteArrayOutputStream();
+ if (binary)
+ ManagedFile.transferWithProgress(inputStream, bos, (long)size, monitor, "");
+ else
+ ManagedFile.transferText(inputStream, bos, (long)size, monitor, "", false);
+ } catch (IOException ex) {
+ throw ManagedFile.wrapException(ex);
+ }
+ }
+
+ /**
+ * @see IManagedFile#getTimeStamp()
+ */
+ public String getTimeStamp() throws CVSFileNotFoundException {
+ return null;
+ }
+
+ /**
+ * @see IManagedFile#setTimeStamp(String)
+ */
+ public void setTimeStamp(String date) throws CVSException {
+ }
+
+ /**
+ * @see IManagedFile#isDirty()
+ */
+ public boolean isDirty() throws CVSException {
+ return false;
+ }
+
+ /**
+ * @see IManagedFile#moveTo(IManagedFile)
+ */
+ public void moveTo(IManagedFile mFile) throws CVSException, ClassCastException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedFile#getContent()
+ */
+ public String[] getContent() throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedResource#getRemoteLocation(IManagedFolder)
+ */
+ public String getRemoteLocation(IManagedFolder stopSearching) throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see Comparable#compareTo(Object)
+ */
+ public int compareTo(Object arg0) {
+ return 0;
+ }
+
+ /**
+ * Return an InputStream which contains the contents of the remote file.
+ */
+ public InputStream getCachedContents() {
+ return new ByteArrayInputStream(bos.toByteArray());
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFolder.java
new file mode 100644
index 000000000..e7b1fca06
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedFolder.java
@@ -0,0 +1,173 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+
+/**
+ * This class can be used to pass an empty folder to CVS in order to see
+ * what children the folder has. The command equivalent where this is applicable
+ * is "cvs -n update" when in a CVS managed directory.
+ *
+ * If given the name of a child file, this class can also be used to fetch the
+ * contents of the file from the server. This is accomplished by associating
+ * a single instance of RemoteManagedFile with the RemoteManagedFolder.
+ */
+public class RemoteManagedFolder extends RemoteManagedResource implements IManagedFolder {
+
+ // NIK: Comment for the "one child" solution ?
+ private RemoteManagedFile child;
+ protected String remote;
+
+ public RemoteManagedFolder(String name, ICVSRepositoryLocation repository, String remote) {
+ this(name, repository, remote, null);
+ }
+
+ public RemoteManagedFolder(String name, ICVSRepositoryLocation repository, String remote, String child) {
+ super(name, null, repository);
+ this.remote = remote;
+ if (child != null)
+ this.child = new RemoteManagedFile(child, this, repository);
+ }
+
+ /**
+ * @see IManagedFolder#getFolders()
+ */
+ public IManagedFolder[] getFolders() throws CVSException {
+ return new IManagedFolder[0];
+ }
+
+ /**
+ * @see IManagedFolder#getFiles()
+ */
+ public IManagedFile[] getFiles() throws CVSException {
+ if (child == null)
+ return new IManagedFile[0];
+ else
+ return new IManagedFile[] {child};
+ }
+
+ /**
+ * @see IManagedFolder#getFolder(String)
+ */
+ public IManagedFolder getFolder(String name) throws CVSException {
+ if (name.equals(Client.CURRENT_LOCAL_FOLDER) || name.equals(Client.CURRENT_LOCAL_FOLDER + Client.SERVER_SEPARATOR))
+ return this;
+ throw new CVSException(Policy.bind("RemoteManagedFolder.invalidChild", new Object[] {name}));
+ }
+
+ /**
+ * @see IManagedFolder#getFile(String)
+ */
+ public IManagedFile getFile(String name) throws CVSException {
+ return (IManagedFile)getChild(name);
+ }
+
+ /**
+ * @see IManagedResource#isFolder()
+ */
+ public boolean isFolder() {
+ return true;
+ }
+
+ /**
+ * @see IManagedFolder#childExists(String)
+ */
+ public boolean childExists(String path) {
+ try {
+ return getChild(path) != null;
+ } catch (CVSException e) {
+ return false;
+ }
+ }
+
+ /**
+ * @see IManagedFolder#getChild(String)
+ */
+ public IManagedResource getChild(String path) throws CVSException {
+ if (path.equals(Client.CURRENT_LOCAL_FOLDER))
+ return this;
+ if ((child != null) && (path.equals(child.getName())))
+ return child;
+ throw new CVSException(Policy.bind("RemoteManagedFolder.invalidChild", new Object[] {name}));
+ }
+
+ /**
+ * @see IManagedFolder#mkdir()
+ */
+ public void mkdir() throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedFolder#flush(boolean)
+ */
+ public void flush(boolean deep) {
+ }
+
+ /**
+ * @see IManagedFolder#getFolderInfo()
+ */
+ public FolderProperties getFolderInfo() throws CVSException {
+ return new FolderProperties(repository.getLocation(), remote, false);
+ }
+
+ /**
+ * @see IManagedFolder#setFolderInfo(FolderProperties)
+ */
+ public void setFolderInfo(FolderProperties folderInfo) throws CVSException {
+ }
+
+ /**
+ * @see IManagedFolder#setProperty(String, String[])
+ */
+ public void setProperty(String key, String[] content) throws CVSException {
+ }
+
+ /**
+ * @see IManagedFolder#unsetProperty(String)
+ */
+ public void unsetProperty(String key) throws CVSException {
+ }
+
+ /**
+ * @see IManagedFolder#getProperty(String)
+ */
+ public String[] getProperty(String key) throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedFolder#isCVSFolder()
+ */
+ public boolean isCVSFolder() throws CVSException {
+ return true;
+ }
+
+ /**
+ * @see IManagedFolder#acceptChildren(IManagedVisitor)
+ */
+ public void acceptChildren(IManagedVisitor visitor) throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedResource#getRemoteLocation(IManagedFolder)
+ */
+ public String getRemoteLocation(IManagedFolder stopSearching) throws CVSException {
+ return repository.getRootDirectory() + Client.SERVER_SEPARATOR + remote;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedResource.java
new file mode 100644
index 000000000..00851b45a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteManagedResource.java
@@ -0,0 +1,111 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+
+/**
+ * This class is the root of a hierarchy of IManagedResource which allows the retrieval
+ * of information about remote resources without requiring the resources to exist locally.
+ * As such, only the required methods of IManagedResource have implementations.
+ */
+public abstract class RemoteManagedResource implements IManagedResource {
+
+ protected String name;
+ protected IManagedFolder parent;
+ protected ICVSRepositoryLocation repository;
+
+ protected RemoteManagedResource(String name, IManagedFolder parent, ICVSRepositoryLocation repository) {
+ this.name = name;
+ this.repository = repository;
+ this.parent = parent;
+ }
+
+ /**
+ * @see IManagedResource#getRelativPath(IManagedFolder)
+ */
+ public String getRelativePath(IManagedFolder ancestor) throws CVSException {
+ if (ancestor == this)
+ return getName();
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see IManagedResource#isFolder()
+ */
+ public boolean isFolder() {
+ return false;
+ }
+
+ /**
+ * @see IManagedResource#delete()
+ */
+ public void delete() {
+ }
+
+ /**
+ * @see IManagedResource#exists()
+ */
+ public boolean exists() {
+ return true;
+ }
+
+ /**
+ * @see IManagedResource#getParent()
+ */
+ public IManagedFolder getParent() {
+ return null;
+ }
+
+ /**
+ * @see IManagedResource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see IManagedResource#isIgnored()
+ */
+ public boolean isIgnored() throws CVSException {
+ return false;
+ }
+
+ /**
+ * @see IManagedResource#isManaged()
+ */
+ public boolean isManaged() throws CVSException {
+ return true;
+ }
+
+ /**
+ * @see IManagedResource#accept(IManagedVisitor)
+ */
+ public void accept(IManagedVisitor visitor) throws CVSException {
+ // We need to do nothing here
+ }
+
+ /**
+ * @see IManagedResource#unmanage()
+ */
+ public void unmanage() throws CVSException {
+ throw new CVSException(Policy.bind("RemoteManagedResource.invalidOperation"));
+ }
+
+ /**
+ * @see Comparable#compareTo(Object)
+ */
+ public int compareTo(Object arg0) {
+ return 0;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteResource.java
new file mode 100644
index 000000000..0df743978
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteResource.java
@@ -0,0 +1,89 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.ccvs.core.IRemoteResource;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+
+/**
+ * The purpose of this class and its subclasses is to implement the corresponding
+ * IRemoteResource interfaces for the purpose of communicating information about
+ * resources that reside in a CVS repository but have not necessarily been loaded
+ * locally.
+ */
+public abstract class RemoteResource extends PlatformObject implements IRemoteResource {
+
+ protected String name;
+ protected RemoteFolder parent;
+ protected IRemoteRoot root;
+ protected String tag;
+
+ protected RemoteResource(RemoteFolder parent, String name) {
+ this(parent, name, null);
+ }
+
+ protected RemoteResource(RemoteFolder parent, String name, String tag) {
+ this.parent = parent;
+ this.name = name;
+ this.tag = tag;
+ }
+
+ /**
+ * @see IRemoteResource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see IRemoteResource#getParent()
+ */
+ public IRemoteFolder getParent() {
+ return parent;
+ }
+
+ /**
+ * Return the IRemoteRoot that is the ancestor of the receiver
+ */
+ public IRemoteRoot getRemoteRoot() {
+ return root;
+ }
+
+ /**
+ * Get the full path for the receiver, starting at the root
+ */
+ public String getFullPath() {
+ String parentPath = parent.getFullPath();
+ if (parentPath.length() == 0)
+ return getName();
+ else
+ return parentPath + Client.SERVER_SEPARATOR + getName();
+ }
+
+ /**
+ * Return the CVSRepositoryLocation representing the remote repository
+ */
+ public CVSRepositoryLocation getConnection() {
+ return parent.getConnection();
+ }
+
+ protected List getLocalOptionsForTag() {
+ List localOptions = new ArrayList();
+ if ((tag != null) && (!tag.equals("HEAD"))) {
+ localOptions.add(Client.TAG_OPTION);
+ localOptions.add(tag);
+ }
+ return localOptions;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteRoot.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteRoot.java
new file mode 100644
index 000000000..b58535ee3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteRoot.java
@@ -0,0 +1,85 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+
+/**
+ * This class provides the implementation of IRemoteRoot
+ */
+public class RemoteRoot extends RemoteFolder implements IRemoteRoot {
+
+ private CVSRepositoryLocation location;
+
+ /**
+ * Constructor for RemoteRoot.
+ * @param parent
+ * @param name
+ */
+ public RemoteRoot(CVSRepositoryLocation location) {
+ super(null, location.getLocation(), null);
+ this.location = location;
+ }
+
+ /**
+ * Return the CVSRepositoryLocation representing the remote repository
+ */
+ public CVSRepositoryLocation getConnection() {
+ return location;
+ }
+
+ /**
+ * @see IRemoteRoot#getConnectionMethod()
+ */
+ public String getConnectionMethod() {
+ return location.getMethod().getName();
+ }
+
+ /**
+ * Get the full path for the receiver, starting at the root
+ */
+ public String getFullPath() {
+ return "";
+ }
+
+ /**
+ * @see IRemoteRoot#getHost()
+ */
+ public String getHost() {
+ return location.getHost();
+ }
+
+ /**
+ * @see IRemoteRoot#getPort()
+ */
+ public int getPort() {
+ return location.getPort();
+ }
+
+ /**
+ * @see IRemoteRoot#getRepositoryPath()
+ */
+ public String getRepositoryPath() {
+ return location.getRootDirectory();
+ }
+
+ /**
+ * @see IRemoteResource#getType()
+ */
+ public int getType() {
+ return ROOT;
+ }
+
+ /**
+ * @see IRemoteRoot#getUser()
+ */
+ public String getUser() {
+ return location.getUsername();
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ResourceFactory.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ResourceFactory.java
new file mode 100644
index 000000000..1210b0e0d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/ResourceFactory.java
@@ -0,0 +1,185 @@
+package org.eclipse.team.internal.ccvs.core.resources;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.CVSFileNotFoundException;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.CVSFile;
+import org.eclipse.team.internal.ccvs.core.resources.CVSFolder;
+import org.eclipse.team.internal.ccvs.core.resources.CVSResource;
+import org.eclipse.team.internal.ccvs.core.resources.ManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.ManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.ManagedResource;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.internal.ccvs.core.*;
+
+/**
+ * This class is the way to access the current implementations of
+ * ICVSResources and IMangedResources.
+ *
+ * All methods with the word "Temp" in it, get a resource that is
+ * relative to a tempFolder rather then the root of the system.
+ */
+public class ResourceFactory {
+
+ public static final File TEMP_ROOT = new File("C:\\temp");
+
+ public static String getPath(File ioFile) {
+ return ioFile.getAbsolutePath();
+ }
+
+ public static String getPath(ICVSResource cvsResource) {
+ return getIO(cvsResource).getAbsolutePath();
+ }
+
+ public static String getPath(IManagedFolder managedResource) {
+ return getIO(managedResource).getAbsolutePath();
+ }
+
+ public static File getIO(ICVSResource cvsResource) throws ClassCastException {
+ return ((CVSResource) cvsResource).getIOResource();
+ }
+
+ public static File getIO(IManagedResource managedResource) throws ClassCastException {
+ return getIO(getCvs(managedResource));
+ }
+
+ public static File getTempIO(String path) {
+ return new File(TEMP_ROOT,path);
+ }
+
+ public static File getIO(String path) {
+ return new File(path);
+ }
+
+ public static ICVSResource getCvs(IManagedResource managedResource) throws ClassCastException {
+ return ((ManagedResource) managedResource).getCVSResource();
+ }
+
+ public static ICVSFolder getCvs(IManagedFolder managedFolder) throws ClassCastException {
+ return (ICVSFolder)((ManagedFolder) managedFolder).getCVSResource();
+ }
+
+ public static ICVSFile getCvs(IManagedFile managedFile) throws ClassCastException {
+ return (ICVSFile)((ManagedFile) managedFile).getCVSResource();
+ }
+
+ public static ICVSResource getCvs(File ioFile) throws CVSException {
+
+ if (!ioFile.exists()) {
+ throw new CVSFileNotFoundException("File not Found " + ioFile);
+ }
+
+ if (ioFile.isDirectory()) {
+ return CVSFolder.createFolderFrom(ioFile);
+ } else if (ioFile.isFile()) {
+ return CVSFile.createFileFrom(ioFile);
+ } else {
+ throw new CVSException("Unexpected error in ResourceFactory");
+ }
+ }
+
+ public static ICVSResource getCvs(String path) throws CVSException {
+ return getCvs(getIO(path));
+ }
+
+ public static ICVSResource getTempCvs(String path) throws CVSException {
+ return getCvs(getTempIO(path));
+ }
+
+ public static ICVSFolder getCvsFolder(File ioFile) throws CVSException {
+ return CVSFolder.createFolderFrom(ioFile);
+ }
+
+ public static ICVSFolder getCvsFolder(String path) throws CVSException {
+ return getCvsFolder(getIO(path));
+ }
+
+ public static ICVSFolder getTempCvsFolder(String path) throws CVSException {
+ return getCvsFolder(getTempIO(path));
+ }
+
+ public static ICVSFolder getCvsFolder(IManagedFolder managedFolder) throws CVSException {
+ return getCvs(managedFolder);
+ }
+
+ public static ICVSFile getCvsFile(String path) throws CVSException {
+ return getCvsFile(getIO(path));
+ }
+
+ public static ICVSFile getTempCvsFile(String path) throws CVSException {
+ return getCvsFile(getTempIO(path));
+ }
+
+ public static ICVSFile getCvsFile(File ioFile) throws CVSException {
+ return CVSFile.createFileFrom(ioFile);
+ }
+
+ public static ICVSFile getCvsFile(IManagedFile managedFile) throws CVSException {
+ return getCvs(managedFile);
+ }
+
+ public static IManagedFolder getManaged(ICVSFolder cvsFolder) {
+ return ManagedFolder.createResourceFrom(cvsFolder);
+ }
+
+ public static IManagedFile getManaged(ICVSFile cvsFile) {
+ return ManagedFolder.createResourceFrom(cvsFile);
+ }
+
+ public static IManagedResource getManaged(ICVSResource cvsResource) {
+ if (cvsResource.isFolder()) {
+ return getManaged((ICVSFolder)cvsResource);
+ } else {
+ return getManaged((ICVSFile)cvsResource);
+ }
+ }
+
+ public static IManagedResource getManaged(File file) throws CVSException {
+ return getManaged(getCvs(file));
+ }
+
+ public static IManagedFolder getManagedFolder(File ioFile) throws CVSException {
+ return getManaged(getCvsFolder(ioFile));
+ }
+
+ public static IManagedFolder getManagedFolder(String path) throws CVSException {
+ return getManagedFolder(getIO(path));
+ }
+
+ public static IManagedFolder getTempManagedFolder(String path) throws CVSException {
+ return getManagedFolder(getTempIO(path));
+ }
+
+ public static IManagedFolder getManagedFolder(ICVSFolder cvsFolder) throws CVSException {
+ return getManaged(cvsFolder);
+ }
+
+ public static IManagedFile getManagedFile(File ioFile) throws CVSException {
+ return getManaged(getCvsFile(ioFile));
+ }
+
+ public static IManagedFile getTempManagedFile(String path) throws CVSException {
+ return getManagedFile(getTempIO(path));
+ }
+
+ public static IManagedFile getManagedFile(String path) throws CVSException {
+ return getManagedFile(getIO(path));
+ }
+
+ public static IManagedFile getManagedFile(ICVSFile cvsFile) throws CVSException {
+ return getManaged(cvsFile);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSFileNotFoundException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSFileNotFoundException.java
new file mode 100644
index 000000000..2a9b1be8b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSFileNotFoundException.java
@@ -0,0 +1,81 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.ResourceStatus;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * This exception represents the attemp to access a file/folder
+ * that did not exist.
+ */
+public class CVSFileNotFoundException extends CVSException {
+
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ super(new ResourceStatus(severity, code, path, message, exception));
+ }
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ IPath path,
+ String message) {
+ this(severity, code, path, message, null);
+ }
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ IPath path,
+ Throwable exception) {
+ this(severity, code, path, null, exception);
+ }
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ String message,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, message, null));
+ }
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ String message) {
+ this(severity, code, message, null);
+ }
+
+ public CVSFileNotFoundException(
+ int severity,
+ int code,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, null, e));
+
+ }
+
+ public CVSFileNotFoundException(String message) {
+ super(new Status(IStatus.ERROR, CVSProviderPlugin.ID, IStatus.ERROR, message, null));
+ }
+
+ public CVSFileNotFoundException(String message, IPath path) {
+ this(message, path, null);
+ }
+
+ public CVSFileNotFoundException(String message, IPath path, Throwable throwable) {
+ this(new ResourceStatus(IStatus.ERROR, path, message, throwable));
+ }
+ public CVSFileNotFoundException(IStatus status) {
+ super(status);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSProperties.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSProperties.java
new file mode 100644
index 000000000..d776319f4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/CVSProperties.java
@@ -0,0 +1,73 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * The CVSProperties are a way to store Properties about a file
+ * or a folder. The CVSProperties have a predefined and not
+ * changing set of keys (you can not set on a key that is not
+ * predefined).<br>
+ * This class is for overloading, also it could be instanceated.
+ */
+public class CVSProperties {
+
+ private Map properties = new HashMap();
+
+ /**
+ * @param supportedKeys the set of predefined keys
+ * to be set by a subclass
+ */
+ public CVSProperties(String[] supportedKeys) {
+ for (int i=0; i<supportedKeys.length; i++) {
+ properties.put(supportedKeys[i],null);
+ }
+ }
+
+ /**
+ * Get the property with the name key.
+ * @return null if key is not in supportedKeys
+ */
+ public String getProperty(String key) {
+ return (String) properties.get(key);
+ }
+
+ /**
+ * Set the value of the property key.
+ * @throws IllegalArgumentException if key is not in
+ * supportedKeys
+ */
+ public String putProperty(String key, String value) throws
+ IllegalArgumentException {
+
+ Assert.isLegal(properties.containsKey(key),
+ Policy.bind("CVSProperties.IllegalKey"));
+
+ return (String) properties.put(key,value);
+ }
+
+ /**
+ * Gives all supported keys as a Set.
+ */
+ public Set keySet() {
+ return properties.keySet();
+ }
+
+ /**
+ * CVSProperties are equal, when keys and values are equal.
+ */
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (!(o instanceof CVSProperties)) {
+ return false;
+ } else {
+ return properties.equals(((CVSProperties)o).properties);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FileProperties.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FileProperties.java
new file mode 100644
index 000000000..a1925b6d8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FileProperties.java
@@ -0,0 +1,287 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Set;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.util.EmptyTokenizer;
+
+/**
+ * A FileProperties bundles all the CVS information stored on disk
+ * about a file such as the name, revision, sever timestamp, etc.
+ * It does not contain information about the physical file in the local file system.
+ */
+public class FileProperties extends CVSProperties {
+
+ private String name;
+
+ private String version;
+ private String timeStamp;
+ private String keywordMode;
+ private String tag;
+
+ public static final String BINARY_TAG = "-kb";
+
+ public static final String ENTRIES = "Entries";
+ public static final String PERMISSIONS = "Permissions";
+ public static final String seperator = "/";
+
+ /**
+ * Construct the FileProperties
+ */
+ public FileProperties() {
+ super(new String[]{ENTRIES,PERMISSIONS});
+ }
+
+ /**
+ * Construct the FileProperties
+ */
+ public FileProperties(String entryLine, String permissions) throws CVSException {
+ this();
+ setEntryLine(entryLine);
+ setPermissions(permissions);
+ }
+
+ /**
+ * Cosntruct a CVS compatible entry line
+ * that can be stored on disk.
+ * @return null if the entry line was not set or set to null
+ */
+ public String getEntryLine() {
+
+ if (name == null) {
+ return null;
+ }
+
+ StringBuffer result = new StringBuffer();
+
+ result.append(seperator);
+ result.append(name);
+ result.append(seperator);
+ result.append(version);
+ result.append(seperator);
+ result.append(timeStamp);
+ result.append(seperator);
+ result.append(keywordMode);
+ result.append(seperator);
+ result.append(tag);
+
+ return result.toString();
+ }
+
+ /**
+ * Cosntruct an entry line for the client that can be sent to the server.
+ * For this the timestamp is not included.
+ * @return null if the entry line was not set or set to null
+ */
+ public String getEntryLineForServer() {
+
+ if (name == null) {
+ return null;
+ }
+
+ StringBuffer result = new StringBuffer();
+
+ result.append(seperator);
+ result.append(name);
+ result.append(seperator);
+ result.append(version);
+ result.append(seperator);
+ result.append(seperator);
+ result.append(keywordMode);
+ result.append(seperator);
+ result.append(tag);
+
+ return result.toString();
+ }
+
+ /**
+ * Set the entry line
+ * @throws CVSException if the entryLine is malformed
+ */
+ public void setEntryLine(String entryLine) throws
+ IllegalArgumentException {
+
+ EmptyTokenizer tokenizer;
+
+ if (entryLine == null) {
+ name = null;
+ return;
+ }
+
+ tokenizer = new EmptyTokenizer(entryLine,seperator);
+
+ Assert.isLegal(entryLine.startsWith(seperator) &&
+ tokenizer.countTokens() == 5,
+ Policy.bind("FileProperties.invalidEntryLine"));
+
+ name = tokenizer.nextToken();
+ version = tokenizer.nextToken();
+ timeStamp = tokenizer.nextToken();
+ keywordMode = tokenizer.nextToken();
+ tag = tokenizer.nextToken();
+ }
+
+ /**
+ * Gets the permissions
+ * @return Returns a String
+ */
+ public String getPermissions() {
+ return getProperty(PERMISSIONS);
+
+ }
+ /**
+ * Sets the permissions
+ * @param permissions The permissions to set
+ */
+ public void setPermissions(String permissions) {
+ putProperty(PERMISSIONS,permissions);
+ }
+
+ /**
+ * Gets the tag
+ * @return Returns a String
+ */
+ public String getTag() {
+ return tag;
+ }
+ /**
+ * Sets the tag
+ * @param tag The tag to set
+ */
+ public void setTag(String tag) {
+ this.tag = tag;
+ }
+
+ /**
+ * Gets the timeStamp
+ * @return Returns a String usually in the format
+ "Thu Oct 18 20:21:13 2001"
+ */
+ public String getTimeStamp() {
+ return timeStamp;
+ }
+ /**
+ * Sets the timeStamp
+ *
+ * @param timeStamp The timeStamp to set
+ has the format "Thu Oct 18 20:21:13 2001" otherwise
+ isDirty is allways true
+ */
+ public void setTimeStamp(String timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+
+ /**
+ * Gets the version
+ * @return Returns a String
+ */
+ public String getVersion() {
+ return version;
+ }
+ /**
+ * Sets the version
+ * @param version the version to set
+ */
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ * Gets the name
+ * @return Returns a String
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * Sets the name
+ * @param name The name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets the keyword mode
+ * @return Returns a String
+ */
+ public String getKeywordMode() {
+ return keywordMode;
+ }
+
+ /**
+ * Sets the keyword mode
+ * @param keywordMode The keyword expansion mode (-kb, -ko, etc.)
+ */
+ public void setKeywordMode(String keywordMode) {
+ this.keywordMode = keywordMode;
+ }
+
+ /**
+ * Special handling for the entries added.
+ * @see CVSProperties#getProperty(String)
+ */
+ public String getProperty(String key) {
+
+ String data;
+
+ if (ENTRIES.equals(key)) {
+
+ return getEntryLine();
+
+ } else {
+ data = super.getProperty(key);
+
+ if (data == null) {
+ return null;
+ }
+
+ if (data.substring(0,1).equals(seperator)) {
+ data = data.substring(data.indexOf(seperator,1)+1);
+ }
+ return data;
+ }
+ }
+
+ /**
+ * Special handling for the entries added.
+ * @see CVSProperties#putProperty(String,String)
+ */
+ public String putProperty(String key, String value)
+ throws IllegalArgumentException {
+
+ if (ENTRIES.equals(key)) {
+ setEntryLine(value);
+ return value;
+ } else {
+ return super.putProperty(key,value);
+ }
+ }
+
+ /**
+ * Put the entries into the entries-properties. This is
+ * done before equals.
+ */
+ private void putEntries() {
+ super.putProperty(ENTRIES,getEntryLine());
+ }
+
+ public boolean equals(Object o) {
+
+ if (o instanceof FileProperties) {
+ putEntries();
+ ((FileProperties)o).putEntries();
+ }
+
+ return super.equals(o);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FolderProperties.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FolderProperties.java
new file mode 100644
index 000000000..35a5671a6
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/FolderProperties.java
@@ -0,0 +1,113 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+
+/**
+ * FolderProperties bundels the informations about a folder that
+ * are needed for the cvsClient.
+ * It cares about loading saving this information
+ * in the folder the container belongs to.
+ */
+public class FolderProperties extends CVSProperties {
+
+ public static final String REPOSITORY = "Repository";
+ public static final String ROOT = "Root";
+ public static final String STATIC = "Entries.Static";
+ public static final String seperator = "/";
+
+ public FolderProperties() {
+ super(new String[]{REPOSITORY,ROOT,STATIC});
+ }
+
+ /**
+ * Create a new FolderProperties and load the information of the cvsFolder
+ * into it.
+ * Does not save the cvsFolder in any way.
+ */
+ public FolderProperties(String root, String repository, boolean staticFolder) {
+ this();
+ setRoot(root);
+ setRepository(repository);
+ setStaticFolder(staticFolder);
+ }
+
+ /**
+ * Gets the repolsitory e.g. "proj1/folder1"
+ * @return Returns a String
+ */
+ public String getRepository() {
+ return getProperty(REPOSITORY);
+ }
+ /**
+ * Sets the repolsitory
+ * @param repolsitory e.g. "proj1/folder1"
+ * @throws CVSException on wrong parameter
+ */
+ public void setRepository(String repository) {
+
+ putProperty(REPOSITORY,repository);
+ }
+
+ /**
+ * Gets the root e.g. ":pserver:nkrambro@fiji:/home/nkrambro/repo"
+ * @return Returns a String
+ */
+ public String getRoot() {
+ return getProperty(ROOT);
+ }
+ /**
+ * Sets the root
+ * @param the Root of the Folder e.g. ":pserver:nkrambro@fiji:/home/nkrambro/repo"
+ * @throws CVSException on wrong parameter
+ */
+ public void setRoot(String root) {
+ putProperty(ROOT,root);
+ }
+
+ /**
+ * Returns the Location of the folder on the server constructed
+ * using the root and repository.
+ *
+ * For example, if the <code>root</code> is ":pserver:username@host:/cvs/root"
+ * and the <code>repository</code> is "proj1/folder1" then <code>getRemoteLocation()</code>
+ * returns "/cvs/root/proj1/folder1".
+ */
+ public String getRemoteLocation() throws CVSException {
+
+ String rootFolder;
+ int start = getRoot().lastIndexOf(":");
+ if (start == -1)
+ throw new CVSException(Policy.bind("FolderProperties.invalidRoot", new Object[] {getRoot()}));
+ rootFolder = getRoot().substring(start + 1);
+
+ return rootFolder + seperator + getRepository();
+ }
+
+ /**
+ * Gets wheter the folder is static
+ * @return Returns a boolean
+ */
+ public boolean getStaticFolder() {
+ return getProperty(STATIC) != null;
+ }
+
+ /**
+ * Sets wheter the folder is static
+ * @param staticFolder The staticFolder to set
+ */
+ public void setStaticFolder(boolean staticFolder) {
+ if (staticFolder) {
+ putProperty(STATIC,"");
+ } else {
+ putProperty(STATIC,null);
+ }
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFile.java
new file mode 100644
index 000000000..fb6558c29
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFile.java
@@ -0,0 +1,93 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * Represents an abstract file.
+ *
+ * @see ICVSResource
+ */
+
+public interface ICVSFile extends ICVSResource {
+
+ /**
+ * Opens the file for reading. Closing the stream
+ * is responsibility of the caller.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws CVSException if it was not possible to open the pipe for any other reason
+ */
+ InputStream getInputStream() throws CVSException;
+
+ /**
+ * Opens the file for writing. Closing the stream
+ * is responsibility of the caller.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws CVSException if it was not possible to open the pipe for any other reason
+ */
+ OutputStream getOutputStream() throws CVSException;
+
+ /**
+ * Get the size of a file
+ *
+ * @return 0 if exists() = false
+ */
+ long getSize();
+
+ /**
+ * Get the timpstamp of the file as a date
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ long getTimeStamp() throws CVSFileNotFoundException;
+
+ /**
+ * Set the timpstamp of the file as a date
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ void setTimeStamp(long date) throws CVSFileNotFoundException;
+
+ /**
+ * Gives the content of the file as a string-array.
+ */
+ String[] getContent() throws CVSException;
+
+ /**
+ * Gives the content of the file as a string-array.
+ *
+ * @param delim is the end of line (e.g. "\n","\n\r")
+ */
+ // void setContent(String[] content, String delim) throws CVSException;
+
+ /**
+ * Move the resource to another location. Does overwrite without
+ * promting.
+ *
+ * @throws CVSException if the move was not successful
+ */
+ void moveTo(ICVSFile file) throws CVSException;
+
+ /**
+ * Get a temporary cvs-file (it does not yet delete on
+ * exit
+ */
+ // public ICVSFile createTempFile() throws CVSException;
+
+ /**
+ * Set the file to read-Only mode.
+ */
+ void setReadOnly();
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFolder.java
new file mode 100644
index 000000000..53e30b81a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSFolder.java
@@ -0,0 +1,158 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+
+/**
+ * Represents an abstract folder.
+ *
+ * @see ICVSResource
+ */
+public interface ICVSFolder extends ICVSResource {
+
+ /**
+ * Does list the whole content of the folder
+ * (files and folders)
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ public ICVSResource[] getResources() throws CVSException;
+
+ /**
+ * Gives all the sub-folders of this folder (excluding the
+ * cvs-folder)
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ public ICVSFolder[] getFolders() throws CVSException;
+
+ /**
+ * Gives all the files in this folder
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ public ICVSFile[] getFiles() throws CVSException;
+
+ /**
+ * create a folder as a subfolder of this
+ * accepts "/" or "\" to make it a sub-sub folder of the
+ * current one. Alle folder on the way, that do not exsist
+ * are generated.
+ *
+ * This is only calling a function in the file-system
+ *
+ */
+ ICVSFolder createFolder(String name) throws CVSException;
+
+ /**
+ * Does create a file in the given folder. Does not accept
+ * any subfolders given in that moment.
+ * If the file does exist, returns the file.
+ *
+ * This is only calling a function in the file-system
+ *
+ */
+ ICVSFile createFile(String name) throws CVSException;
+
+ /**
+ * Return the child resource at the given path relative to
+ * the receiver.
+ * This gets a file child of the current folder. It needs to
+ * contact the fileSystem to figure out whether we have got
+ * an folder or an file.
+ * It is decepated, because it should be possible to create
+ * non-existend files (and then we need the information whether it
+ * is a file or a folder).
+ * Use createFile or createFolder intstead. (Check whether it is
+ * a folder or a file with childIsFolder)
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws CVSFileNotFoundException if childExists(path) = false
+ */
+ ICVSResource getChild(String path) throws CVSException;
+
+ /**
+ * Checks wether the child of the currentFolder with the
+ * name exists.
+ */
+ boolean childExists(String name);
+
+ /**
+ * Checks wether a child is a folder. If the child does not
+ * exist then it automatically does return false.
+ *
+ * @return false if childExists(name) = false
+ */
+ boolean childIsFolder(String name);
+
+ /**
+ * Create the folder if it did not exist before. Does only
+ * work if the direct subfolder did exist.
+ *
+ * @throws CVSException if for some reason it was not possible to create the folder
+ */
+ void mkdir() throws CVSException;
+
+ // ---------- Here starts the property handling ----------
+ /**
+ * This method creats the ability to store properties.
+ * It does create the current folder as well, if it did not
+ * exist before.
+ * At the moment this invokes mkdir, so it craetest the current
+ * folder if it was not there before.
+ * (This is going to change properbly)
+ */
+ void makeCVSFolder() throws CVSException;
+
+ /**
+ * The opposite of makeCVSFolder, delets all the properties of
+ * the folder and the ability to store such
+ */
+ void unmakeCVSFolder();
+
+ /**
+ * Checks if properties are accessable and if so,
+ * whether it has at least the following three properties:
+ * root, repolsitory, entries
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ boolean isCVSFolder() throws CVSFileNotFoundException;
+
+ /**
+ * Attace a property to the folder.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws NoCVSFolderException if isCVSFolder = false
+ */
+ void setProperty(String key, String[] content) throws CVSException;
+
+ /**
+ * Delete a property from a folder. If the property did not exist,
+ * nothing happens.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws NoCVSFolderException if isCVSFolder = false
+ */
+ // void unsetProperty(String key) throws CVSException;
+
+ /**
+ * Get the property of a folder.
+ *
+ * @return the contend of the property if the property does exsist, null otherwise
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws NoCVSFolderException if isCVSFolder = false
+ */
+ String[] getProperty(String key) throws CVSException;
+
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSResource.java
new file mode 100644
index 000000000..dc90f1037
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/ICVSResource.java
@@ -0,0 +1,81 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+
+/**
+ * Represents an abstract file or a folder.
+ *
+ * This can be a acctual file in the local system, webDev
+ * or ftp remote-files etc.
+ *
+ * The interfaces are to be implemented by the user of the
+ * cvsclient.core.
+ *
+ * The handle to an resource-object (like in java.io) does not nessarily
+ * mean that the underlying resource exists. The function exists()
+ * checks that. Many Operations give an CVSFileNotFoundException if the
+ * file is not there.
+ */
+
+public interface ICVSResource extends Comparable {
+
+ /**
+ * The seperator that is used for giving and
+ * reciving pathnames
+ */
+ //MV: This should be in CVSResource, not ICVSResource
+ public static final String seperator = File.separator;
+
+ /**
+ * Gives the platform dependend Path of the file back.
+ * Should be used for monitoring only.
+ */
+ String getPath();
+
+ /**
+ * Indicates whether the object is a file or a folder
+ */
+ boolean isFolder();
+
+ /**
+ * Delete the resource.
+ * In case of folder, with all the subfolders and files.
+ *
+ * Deleting a non-existing resourec does nothing.
+ */
+ void delete();
+
+ /**
+ * Give the folder that contains this resource.
+ *
+ * The behavior is unspecified as soon as isCVSFolder() = false
+ */
+ ICVSFolder getParent();
+
+ /**
+ * Give the name of the file back
+ * e.g. "folder1" for "C:\temp\folder1\"
+ */
+ public String getName();
+
+ /**
+ * Check if the file exists in the underlying fileSystem
+ */
+ boolean exists();
+
+ /**
+ * Clears all the information in the cache, if a cache exists.
+ */
+ void clearCache(boolean deep) throws CVSException ;
+
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFile.java
new file mode 100644
index 000000000..0a8b5cd28
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFile.java
@@ -0,0 +1,117 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * The managedFile gives you an FileProperties-Object, that
+ * conains CVS-specific information about a file.
+ *
+ * It also provides sending and reciving contend to/from an
+ * OutputStream/InputStrem.
+ *
+ * @see IManagedResource
+ */
+public interface IManagedFile extends IManagedResource {
+
+ /**
+ * Get the size of a file
+ *
+ * @return 0 if exists() = false
+ */
+ long getSize();
+
+ /**
+ * Get the FileProperties for this file
+ *
+ * Changing the Object has no influence on
+ * the infos of the file, you need to set it.
+ *
+ * @see IManagedFile#setFileInfo(FileProperties)
+ * @return null if the file is not in the entries-list of the parent-folder (can return something also exists() = false)
+ */
+ FileProperties getFileInfo() throws CVSException;
+
+ /**
+ * Set the FileProperties for the file.
+ *
+ * @param if fileInfo == null, the info is removed
+ * @throws CVSException if getName() != fileInfo.getName()
+ */
+ void setFileInfo(FileProperties fileInfo) throws CVSException;
+
+ /**
+ * Send the fileContend to an InputStream.
+ * A progressmonitor monitors this process.
+ *
+ * If not exists() the file is created.
+ *
+ * @throws CVSException if file is contained by an non-existing folder
+ * @throws CVSException if it is not possible to write the file
+ */
+ void sendTo(OutputStream outputStream, IProgressMonitor monitor, boolean binary) throws CVSException;
+
+ /**
+ * Get the fileContend from a stream and put
+ * it into this file.
+ *
+ * @throws CVSFileNotFoundException if not exists()
+ */
+ void receiveFrom(InputStream inputStream, IProgressMonitor monitor, long size, boolean binary, boolean readOnly) throws CVSException;
+
+ /**
+ * Get the timpstamp of the file as a date
+ * the format is going to be like: Thu Oct 18 20:21:13 2001
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ String getTimeStamp() throws CVSFileNotFoundException;
+
+ /**
+ * Set the timpstamp of the file as a date
+ * the format needs to be like: Thu Oct 18 20:21:13 2001
+ *
+ * if the date==null then the current time is used as
+ * timestamp
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws CVSException if the format of the date is not correct
+ */
+ void setTimeStamp(String date) throws CVSException;
+
+ /**
+ * Get if the file has been modified since the last time
+ * saved in the fileEntry
+ *
+ * @return true if !isManaged()
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ boolean isDirty() throws CVSException;
+
+ /**
+ * Move the resource to another location. Does overwrite without
+ * promting.
+ *
+ * @throws CVSException if the move was not successful
+ * @throws ClassCastException if getClass != mFile.getClass
+ */
+ void moveTo(IManagedFile mFile) throws CVSException, ClassCastException;
+
+ /**
+ * Gives the content of the file as a string-array.
+ * This is thought for testing purpose only.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ String[] getContent() throws CVSException;
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFolder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFolder.java
new file mode 100644
index 000000000..fd90457ca
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedFolder.java
@@ -0,0 +1,210 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * The managedFile gives you an FolderProperties-Object, that
+ * conains CVS-specific information about a file. It takes
+ * care about tracking files in every folder and additional
+ * information about these files.
+ *
+ * One of the most important things are getFolders() and
+ * getFiles(). These functions give you files/folders that:<ul>
+ * <li> do exist locally
+ * <li> do exist in the entrie-file of this folder
+ * <li> do satisfy both of the above criteria
+ * </ul>
+ *
+ * You can find out wether a resource locally exists with exists()
+ * and you can find out whether it is in the entries with isManaged().
+ *
+ * Folders that do not exist, can only be generated by deleting the
+ * folder. It is not of much use, because the FolderProperties is saved in the
+ * folder itself and therefore can not be saved as long as the folder
+ * does not exist.
+ *
+ * @see IManagedResource
+ */
+public interface IManagedFolder extends IManagedResource {
+
+ /**
+ * Get all the folders in the current folder.
+ * There are three types of folders:<ul>
+ * <li> Does exist() but not isManaged() (local, non registered folder. Should we ignore?)
+ * <li> Does not exist() but isManaged() (deleted folder?)
+ * <li> Does exist() and isManaged() (normal registerd file)
+ * </ul>
+ *
+ * @throws CVSException if not exists()
+ */
+ IManagedFolder[] getFolders() throws CVSException;
+
+ /**
+ * Get all the files in the current folder
+ * There are three types of files:<ul>
+ * <li> Does exist() but not isManaged() (local, non registerd file)
+ * <li> Does not exist() but isManaged() (deleted file without remove or commit?)
+ * <li> Does exist() and isManaged() (normal registerd file)
+ * </ul>
+ *
+ * @throws CVSException if not exists()
+ */
+ IManagedFile[] getFiles() throws CVSException;
+
+ /**
+ *
+ * => is about to be renamed to getFolder()
+ *
+ * @throws CVSException if not exists()
+ */
+ IManagedFolder getFolder(String name) throws CVSException;
+
+ /**
+ * Does create a file in the given folder. Does not accept
+ * any subfolders given in that moment.
+ *
+ * => is about to be renamed to getFile()
+ *
+ * @throws CVSException if not exists()
+ */
+ IManagedFile getFile(String name) throws CVSException;
+
+ /**
+ * States if the resource at the given path relative to this
+ * folder does exist.
+ */
+ boolean childExists(String path);
+
+ /**
+ * Return the child resource at the given path relative to
+ * the receiver.
+ *
+ * @throws CVSException if childExists(path) = false
+ */
+ IManagedResource getChild(String path) throws CVSException;
+
+ /**
+ * Create the folder if it did not exist before. Does only
+ * work if the direct subfolder did exist.
+ *
+ * @throws CVSException if for some reason it was not possible to create the folder
+ */
+ void mkdir() throws CVSException;
+
+ /**
+ * Return the child folder at the given path relative to
+ * the receiver.
+ *
+ * @throws CVSException if childExists(path) = false
+ * @throws CVSException if getChild(path).isFolder = false
+ */
+ // IManagedFolder getFolder(String path) throws CVSException;
+
+ /**
+ * Return the child file at the given path relative to
+ * the receiver.
+ *
+ * @throws CVSException if childExists(path) = false
+ * @throws CVSException if getChild(path).isFolder = true
+ */
+ // IManagedFile getFile(String path) throws CVSException;
+
+ /**
+ * Does write all cached infromation to the file-system.
+ *
+ * @param deep=true => is called recursively for all subfolders
+ */
+ void flush(boolean deep);
+
+ /**
+ * Get Infos about the folder.
+ *
+ * Changing the Object has no influence on
+ * the infos of the file, you need to set it.
+ *
+ * @see IManagedFolder#setFolderInfo(FolderProperties)
+ * @return null if isManaged() == false (but not returns non-null if isManaged() == true)
+ * @return null if exists() == false (but not returns non-null if exists() == true)
+ */
+ FolderProperties getFolderInfo() throws CVSException;
+
+ /**
+ * Set the infos of the folder.
+ *
+ * @param if folderInfo == null, the info is removed
+ * @throws CVSException if (folderInfo!=null & exists()==false)
+ */
+ void setFolderInfo(FolderProperties folderInfo) throws CVSException;
+
+ /**
+ * Add a subFolder to the entries
+ */
+ // we don't need that, we are going to do that, when
+ // we set the FolderProperties of the parent-folder
+ // void addFolder(String name);
+
+ /**
+ * Remove a subFolder from the entries
+ */
+ // we don't need that, we are going to do that, when
+ // we set the FolderProperties of the parent-folder
+ // void removeFolder(String name);
+
+ /**
+ * Look if a subFolder is in the entries
+ */
+ // we can ask for createFolder(name).isManaged() insead
+ // we do not need it
+ // boolean containsFolder(String name);
+
+ /**
+ * Attace a property to the folder.
+ *
+ * @param content==null has the same effect as unsetProperty,
+ content==String[0] creates an empty Property
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ void setProperty(String key, String[] content) throws CVSException;
+
+ /**
+ * Delete a property from a folder. If the property did not exist,
+ * nothing happens.
+ *
+ * @throws CVSFileNotFoundException if exists() = false
+ */
+ // void unsetProperty(String key) throws CVSException;
+
+ /**
+ * Get the property of a folder.
+ *
+ * @return the contend of the property if the property does exsist, null otherwise
+ * @throws CVSFileNotFoundException if exists() = false
+ * @throws NoCVSFolderException if isCVSFolder() = false of the underling CVSFolder
+ */
+ String[] getProperty(String key) throws CVSException;
+
+ /**
+ * Gives the ability of the folder to store properties back.
+ *
+ * (isCVSFolder() == true) <=> (getFolderInfo() != null)
+ */
+ boolean isCVSFolder() throws CVSException;
+
+ /**
+ * Visitor-Pattern.<br>
+ *
+ * Accepts the visitor on all files and all subFolder
+ * in the folder.
+ * First all the files are to be called then all the folders are
+ * to be called.
+ */
+ public void acceptChildren(IManagedVisitor visitor) throws CVSException;
+
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedResource.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedResource.java
new file mode 100644
index 000000000..bff76def0
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedResource.java
@@ -0,0 +1,121 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * An managedResource is an interface for an resource the cvs-client
+ * accepts. It does provide storage of certain properties for every resource.
+ *
+ * It also provides a list of resources within every folder, that is
+ * independend of underlign file-system
+ */
+public interface IManagedResource extends Comparable {
+
+ /**
+ * Do we save the properties after every set
+ * of an info ? (AUTO_SAVE=true <=> no caching)
+ */
+ public static final boolean AUTO_SAVE = true;
+
+ /**
+ * The seperator that is used for giving and
+ * receiving pathnames
+ */
+ public static final String separator = "/";
+
+ /**
+ * Gives the path from the root folder to this folder.
+ *
+ * root.getChild(getRelativePath()).equals(this)
+ *
+ * @throws CVSException if getClass() != ancestor.getClass()
+ * @throws CVSException if ! absolutePathOf(this).startsWith(absolutePathOf(ancestor))
+ */
+ String getRelativePath(IManagedFolder ancestor) throws CVSException;
+
+ /**
+ * Indicates whether the object is a file or a folder
+ */
+ boolean isFolder();
+
+ /**
+ * Delete the resource.
+ * In case of folder, with all the subfolders and files
+ */
+ void delete();
+
+ /**
+ * Tells if the underlying resource does exist.
+ * (Maybe it has been delted in between)
+ */
+ boolean exists();
+
+ /**
+ * Give the folder that contains this resource.
+ *
+ * If not isManaged() then the result of the operation is
+ * unsepecified.
+ */
+ IManagedFolder getParent();
+
+ /**
+ * Give the name of the file back
+ * e.g. "folder1" for "C:\temp\folder1\"
+ */
+ public String getName();
+
+ /**
+ * Answer whether the resource is to be ignored or not
+ */
+ public boolean isIgnored() throws CVSException;
+
+ /**
+ *
+ * Answer whether the resource is managed by it's parent. In CVS
+ * terms, this meanes the parent folder has an entry for the given
+ * resource in its CVS/Entries file.
+ *
+ * @see IManagedFolder#isCVSFolder()
+ * A folder may not have an FolderProperties also it is
+ * managed. This could only happen if the folder has
+ * been removed locally.
+ *
+ */
+ public boolean isManaged() throws CVSException;
+
+ /**
+ * Unmanage the given resource by purging any CVS information
+ * associated with the resource.
+ */
+ public void unmanage() throws CVSException;
+
+ /**
+ * Vistor-Pattern.<br>
+ *
+ * Accept a vistor to this resource.
+ * To be implemented in file and folder (otherwise
+ * we do not know whether to call visitFolder or
+ * visitFile)
+ */
+ public void accept(IManagedVisitor visitor) throws CVSException;
+
+ /**
+ * Get the remote location of a file either by reading it out of the
+ * file-info or by asking the parent-directory for it and appending the
+ * own name (recursivly).It stops recuring when it hits stopSearching.<br>
+ *
+ * If you want to get the remoteLocation of the currentFolder only then
+ * use it with getRemoteLocation(this).
+ *
+ * @return null if there was no remote-location until the folder stopSerarching
+ * @throws NullPointerException if stopSearching in not an ancestor of this
+ */
+ public String getRemoteLocation(IManagedFolder stopSearching) throws CVSException;
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedVisitor.java
new file mode 100644
index 000000000..60c6c8bf4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/IManagedVisitor.java
@@ -0,0 +1,19 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * Interface for an visitor of the IManagedResources.
+ */
+public interface IManagedVisitor {
+
+ public void visitFile(IManagedFile file) throws CVSException;
+ public void visitFolder(IManagedFolder folder) throws CVSException;
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/NotCVSFolderException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/NotCVSFolderException.java
new file mode 100644
index 000000000..a1ff9c60c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/api/NotCVSFolderException.java
@@ -0,0 +1,82 @@
+package org.eclipse.team.internal.ccvs.core.resources.api;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.ResourceStatus;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * This Exception indicates that you have tried to call
+ * a CVSFolder-Specific function on a folder that is not
+ * (yet) a cvs-folder.
+ */
+public class NotCVSFolderException extends CVSException {
+
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ IPath path,
+ String message,
+ Throwable exception) {
+ super(new ResourceStatus(severity, code, path, message, exception));
+ }
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ IPath path,
+ String message) {
+ this(severity, code, path, message, null);
+ }
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ IPath path,
+ Throwable exception) {
+ this(severity, code, path, null, exception);
+ }
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ String message,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, message, null));
+ }
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ String message) {
+ this(severity, code, message, null);
+ }
+
+ public NotCVSFolderException(
+ int severity,
+ int code,
+ Exception e) {
+ super(new Status(severity, CVSProviderPlugin.ID, code, null, e));
+
+ }
+
+ public NotCVSFolderException(String message) {
+ super(new Status(IStatus.ERROR, CVSProviderPlugin.ID, IStatus.ERROR, message, null));
+ }
+
+ public NotCVSFolderException(String message, IPath path) {
+ this(message, path, null);
+ }
+
+ public NotCVSFolderException(String message, IPath path, Throwable throwable) {
+ this(new ResourceStatus(IStatus.ERROR, path, message, throwable));
+ }
+ public NotCVSFolderException(IStatus status) {
+ super(status);
+ }
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CheckedIn.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CheckedIn.java
new file mode 100644
index 000000000..a4cce3f57
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CheckedIn.java
@@ -0,0 +1,96 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * Response to a "Checked-in" form the server.
+ * Does save the EntryLine that comes with the
+ * response.
+ */
+class CheckedIn extends ResponseHandler {
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Checked-in";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String entryLine;
+ String localDirectory;
+ String repositoryFilename;
+ String fileName;
+ boolean changeFile;
+
+ IManagedFile mFile;
+ IManagedFolder mParent;
+ FileProperties fileInfo;
+
+ // Read the info associated with the Updated response
+ localDirectory = connection.readLine();
+ repositoryFilename = connection.readLine();
+ entryLine = connection.readLine();
+
+ // Get the local file
+ fileName = repositoryFilename.substring(repositoryFilename.lastIndexOf("/") + 1);
+ mParent = mRoot.getFolder(localDirectory);
+ mFile = mParent.getFile(fileName);
+
+ // Set the entry and do not change the permissions
+ // CheckIn can be an response on adding a new file,
+ // so we can not rely on having a fileInfo ...
+
+ // In this case we do not save permissions, but as we
+ // haven't got anything from the server we do not need
+ // to. Saveing permissions is only cashing information
+ // from the server.
+ changeFile = mFile.getFileInfo() == null;
+
+ // If the file is not on disk then we have got an removed
+ // file and therefore a file that is dirty after the check-in
+ // as well
+ changeFile = changeFile || !mFile.exists();
+
+ if (changeFile) {
+ fileInfo = new FileProperties();
+ } else {
+ fileInfo = mFile.getFileInfo();
+ }
+
+ fileInfo.setEntryLine(entryLine);
+
+ if (changeFile) {
+ fileInfo.setTimeStamp(DUMMY_TIMESTAMP);
+ } else {
+ fileInfo.setTimeStamp(mFile.getTimeStamp());
+ }
+
+ mFile.setFileInfo(fileInfo);
+
+ Assert.isTrue(changeFile == mFile.isDirty());
+
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CopyHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CopyHandler.java
new file mode 100644
index 000000000..fb0afa17a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/CopyHandler.java
@@ -0,0 +1,67 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * Reacts on the "Copy-file"-Response of the server.
+ * Just copies the file as suggested by the server.<br>
+ * NOTE: The handler acctually copies the file, what does not
+ * seem to cause a problem, because it is only used for
+ * making copies for security on a merge.
+ */
+class CopyHandler extends ResponseHandler {
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Copy-file";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, PrintStream, IManagedFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String fileName;
+
+ IManagedFolder mParent;
+ IManagedFile mFile;
+ IManagedFile mNewFile;
+
+ // Read the info associated with the Updated response
+ String localDirectory = connection.readLine();
+ String repositoryFilename = connection.readLine();
+ String newFilename = connection.readLine();
+
+ // Get the local file
+ fileName = repositoryFilename.substring(repositoryFilename.lastIndexOf("/") + 1);
+ mParent = mRoot.getFolder(localDirectory);
+ mFile = mParent.getFile(fileName);
+
+ Assert.isTrue(mParent.exists() && mParent.isCVSFolder());
+ Assert.isTrue(mFile.exists() && mFile.isManaged());
+
+ // Move the file to newFile (we know we do not need the
+ // original any more anyway)
+ mNewFile = mParent.getFile(newFilename);
+ mFile.moveTo(mNewFile);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DefaultHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DefaultHandler.java
new file mode 100644
index 000000000..5236d1ee7
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DefaultHandler.java
@@ -0,0 +1,57 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * Response to the response that is not handled otherwise
+ *
+ * Reads the rest of the line, checks for error and dumps
+ * everything that it has read.
+ */
+class DefaultHandler extends ResponseHandler {
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "dump";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ // FIXME look wether we need this or if the connection has
+ // appropiate handling
+
+ // Check if the command is truncated because of
+ // an closed connection
+ if (connection.isClosed()) {
+ throw new CVSException(Policy.bind("DefaultHandler.connectionClosed"));
+ }
+
+ if (connection.getLastUsedDelimiterToken() == BLANK_DELIMITER) {
+ connection.readLine();
+ }
+ }
+
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DumpHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DumpHandler.java
new file mode 100644
index 000000000..a46e577e3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/DumpHandler.java
@@ -0,0 +1,48 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * The Dump-Handler reads the rest of the
+ * line out of the connection and ignores it
+ */
+class DumpHandler extends ResponseHandler {
+
+ String name;
+
+ public DumpHandler(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ connection.readLine();
+
+ }
+
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/IResponseHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/IResponseHandler.java
new file mode 100644
index 000000000..8efea1069
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/IResponseHandler.java
@@ -0,0 +1,41 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * Represents an handler for a specific response of the
+ * server.
+ * e.g. an handler could get information out of the pipe
+ * and write it down to disk.
+ */
+public interface IResponseHandler {
+
+ static final char BLANK_DELIMITER = ' ';
+
+ /**
+ * Returns the responses type. This is the name of
+ * the CVS response in <code>String</code> form.
+ */
+ public String getName();
+
+ /**
+ * Handle the given response from the server.
+ */
+ public void handle(Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException;
+}
+
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/MessageOutputHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/MessageOutputHandler.java
new file mode 100644
index 000000000..992714451
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/MessageOutputHandler.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * The MessageOutputHandler sends the whoole line (incl. read token)
+ * to the messageOutput (up the userof the client)
+ */
+class MessageOutputHandler extends ResponseHandler {
+
+ String name;
+
+ public MessageOutputHandler(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ messageOutput.println(connection.readLine());
+
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ModTimeHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ModTimeHandler.java
new file mode 100644
index 000000000..b4218c7c4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ModTimeHandler.java
@@ -0,0 +1,92 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.text.ParseException;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.util.FileDateFormat;
+import org.eclipse.team.internal.ccvs.core.util.ServerDateFormat;
+
+/**
+ * The ModTimeHandler saves the modification time given from the
+ * server.<br>
+ * The last modification time can be asked by other handlers through
+ * pullLastTime(), which nulls the buffer.<br>
+ * (The server does not need to send timestamps, then we want to use
+ * the local time rather than the last time send)
+ */
+class ModTimeHandler extends ResponseHandler {
+
+ private String modTime;
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Mod-time";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, IManagedFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String unConverted = connection.readLine();
+
+ modTime = convertStamp(unConverted,true);
+ }
+
+ /**
+ * Returns the last modification-time that it got from the server.
+ *
+ * @return null, if somebody else pulled the time before
+ */
+ public String pullLastTime() {
+ String oldTime = modTime;
+ modTime = null;
+ return oldTime;
+ }
+
+ /**
+ * Converts Timestamps between: <br>
+ * the server used format ("18 Oct 2001 20:21:13 -0350")<br>
+ * the format in the filesystem ("Thu Oct 18 20:21:13 2001")
+ */
+ private static String convertStamp(String stamp, boolean toFile) throws CVSException {
+
+ long dateInMsec;
+ ServerDateFormat serverFormater = new ServerDateFormat();
+ FileDateFormat fileFormater = new FileDateFormat();
+
+ try {
+ if (toFile) {
+ dateInMsec = serverFormater.parseMill(stamp);
+ return fileFormater.formatMill(dateInMsec);
+ } else {
+ dateInMsec = fileFormater.parseMill(stamp);
+ return serverFormater.formatMill(dateInMsec);
+ }
+ } catch (ParseException e) {
+
+ throw new CVSException(Policy.bind("ModTimeHandler.invalidFormat", stamp),e);
+
+ // if the timestamp is not parseable we have got something of the
+ // kind we properbly do not want to parse, so we just return the
+ // text it was before
+ // return stamp;
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/RemoveEntry.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/RemoveEntry.java
new file mode 100644
index 000000000..9295c4686
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/RemoveEntry.java
@@ -0,0 +1,61 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * This class responds to the Removed-response of the server<br>
+ * It removes the file from both the entries of the parent-folder.
+ * This happen, when the folder has allready been removed locally
+ * what happens on a checkin that includes a removed file.
+ */
+class RemoveEntry extends ResponseHandler {
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Remove-entry";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, PrintStream, IManagedFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String fileName;
+
+ IManagedFolder mParent;
+ IManagedFile mFile;
+
+ // Read the info associated with the Updated response
+ String localDirectory = connection.readLine();
+ String repositoryFilename = connection.readLine();
+
+ // Get the local file
+ fileName = repositoryFilename.substring(repositoryFilename.lastIndexOf("/") + 1);
+ mParent = mRoot.getFolder(localDirectory);
+ mFile = mParent.getFile(fileName);
+
+ // NOTE: Should we do something here other than throw a run-time exception
+ Assert.isTrue(mParent.exists() && !mFile.exists());
+
+ mFile.setFileInfo(null);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Removed.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Removed.java
new file mode 100644
index 000000000..0dfbf13b4
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Removed.java
@@ -0,0 +1,62 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * This class responds to the Removed-response of the server<br>
+ * It removes the file from both the entries of the parent-folder
+ * and from the local filesystem.
+ */
+class Removed extends ResponseHandler {
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return "Removed";
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, PrintStream, IManagedFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String fileName;
+
+ IManagedFolder mParent;
+ IManagedFile mFile;
+
+ // Read the info associated with the Updated response
+ String localDirectory = connection.readLine();
+ String repositoryFilename = connection.readLine();
+
+ // Get the local file
+ fileName = repositoryFilename.substring(repositoryFilename.lastIndexOf("/") + 1);
+ mParent = mRoot.getFolder(localDirectory);
+ mFile = mParent.getFile(fileName);
+
+ Assert.isTrue(mFile.exists() && mFile.isManaged());
+
+ // "unmanage" the folder and delete it ...
+ mFile.setFileInfo(null);
+ mFile.delete();
+
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseDispatcher.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseDispatcher.java
new file mode 100644
index 000000000..0a35237b5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseDispatcher.java
@@ -0,0 +1,279 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import org.eclipse.core.internal.utils.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * The ResponseContainer manages the respones of the server and
+ * pipes them to the appropiate handlers.
+ *
+ * It also takes care about registering handlers for response-tokens
+ * form the server. Standard-handlers are loaded on creation.
+ */
+public class ResponseDispatcher {
+
+ public static final String OK = "ok";
+ public static final String ERROR = "error";
+
+ private Hashtable standardResponsePool;
+ private Hashtable replaceResponsePool;
+ private Connection connection;
+ // Idea: private IResponse[] addResponsePool;
+
+ /**
+ * Puts all the Request in a container in order to have access
+ * to them when they come from the stream
+ *
+ * Generic approach to "plug in" new responses just by adding them
+ * to this constructor
+ */
+ public ResponseDispatcher(Connection connection, IResponseHandler[] customHandlers) {
+
+ ModTimeHandler modTimeHandler = new ModTimeHandler();
+
+ this.connection = connection;
+
+ standardResponsePool = new Hashtable();
+ replaceResponsePool = new Hashtable();
+
+ registerStandardHandler(modTimeHandler);
+ registerStandardHandler(new CopyHandler());
+ registerStandardHandler(new RemoveEntry());
+ registerStandardHandler(new Updated(modTimeHandler,true));
+ registerStandardHandler(new Updated(modTimeHandler,false));
+ // registerStandardHandler(new UpdateExisting(modTimeHandler));
+ registerStandardHandler(new UnsupportedHandler("Valid-requests"));
+ registerStandardHandler(new CheckedIn());
+ registerStandardHandler(new Removed());
+ registerStandardHandler(new MessageOutputHandler("M"));
+ registerStandardHandler(new MessageOutputHandler("E"));
+ registerStandardHandler(new StaticHandler(true));
+ registerStandardHandler(new StaticHandler(false));
+ // FIXME: would we need a StickiHandler ?
+
+ if (customHandlers != null) {
+ for (int i=0;i<customHandlers.length;i++) {
+ registerResponseHandler(customHandlers[i]);
+ }
+ }
+
+ }
+
+ /**
+ * Get the handler matching the string. Take it from the replaceResponsePool if
+ * possible, otherwise take it from the standardResponsePool.
+ *
+ * If there is no matching handler at all, return a standard-handler
+ */
+ private IResponseHandler getHandler(String responseToken) {
+
+ IResponseHandler responseHandler = (IResponseHandler) replaceResponsePool.get(responseToken);
+
+ if (responseHandler == null) {
+ responseHandler = (IResponseHandler) standardResponsePool.get(responseToken);
+ }
+
+ if (responseHandler == null) {
+ responseHandler = new DefaultHandler();
+ }
+
+ return responseHandler;
+ }
+
+ /**
+ * Give a list of all registered Responses from the Server.
+ *
+ * (ok, error is added, because they are not fromal
+ * registerd as handler)
+ *
+ */
+ public String makeResponseList() {
+
+ StringBuffer result = new StringBuffer("ok error");
+
+ /* We are only looking into the standardResponsePool
+ all the registerd responses must be here as well,
+ otherwise you are not allowed to register special
+ handler
+ */
+ Iterator elements = standardResponsePool.values().iterator();
+ while (elements.hasNext()) {
+ IResponseHandler handler = (IResponseHandler) elements.next();
+ result.append(' ');
+ result.append(handler.getName());
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Given a token of response from the server, this method
+ * reacts on it and does the appropiate handling with the
+ * responseHandler, that are loaded in it.
+ */
+ public void handle(String responseToken,
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+
+ IResponseHandler responseHandler;
+
+ responseHandler = getHandler(responseToken);
+ responseHandler.handle(connection, messageOutput, mRoot, monitor);
+
+ }
+
+ /**
+ * To register a non-standard responseHandler.
+ *
+ * Replaces the preloaded responshandler for one response of the
+ * server. The name of the replaced response is response.getName().
+ *
+ * If the response is not known to the server, then the call crashes.
+ *
+ */
+ public void registerResponseHandler(IResponseHandler responseHandler) {
+
+ Assert.isNotNull(standardResponsePool.get(responseHandler.getName()));
+ Assert.isTrue(replaceResponsePool.get(responseHandler.getName()) == null);
+
+ replaceResponsePool.put(responseHandler.getName(),responseHandler);
+ }
+
+ /**
+ * To unregister a non-standard responseHandler.
+ *
+ */
+ public void unregisterResponseHandler(IResponseHandler responseHandler) {
+
+ Assert.isNotNull(standardResponsePool.get(responseHandler.getName()));
+ Assert.isNotNull(replaceResponsePool.get(responseHandler.getName()));
+
+ replaceResponsePool.remove(responseHandler.getName());
+ }
+
+ /**
+ * Registers a standard-handler while doing the
+ * init.
+ */
+ private void registerStandardHandler(IResponseHandler responseHandler) {
+
+ Assert.isTrue(standardResponsePool.get(responseHandler.getName()) == null);
+
+ standardResponsePool.put(responseHandler.getName(),responseHandler);
+ }
+
+ /**
+ * Runs the response event loop.
+ *
+ * If OK is in the pipe => stop looping.
+ * In Error is in the pipe => throw error, stop looping.
+ * Something else in the pipe => handle it with handle(response)
+ * and continou looping
+ *
+ * Does the work with the monitor
+ */
+ public void manageResponse(IProgressMonitor monitor,
+ IManagedFolder mRoot,
+ PrintStream messageOutput)
+ throws CVSException {
+
+ // Processing responses contributes 70% of total work.
+ // This depends on the caller to have picked the magic number of 100
+ // as the amount of work for the operation!!!
+ IProgressMonitor subMonitor = Policy.subMonitorFor(monitor, 70);
+
+ // This number can be tweaked if the monitor is judged to move too
+ // quickly or too slowly. After some experimentation this is a good
+ // number for both large projects (it doesn't move so quickly as to
+ // give a false sense of speed) and smaller projects (it actually does
+ // move some rather than remaining still and then jumping to 100).
+ final int TOTAL_WORK = 300;
+ subMonitor.beginTask(Policy.bind("ResponseDispatcher.receiving"), TOTAL_WORK);
+
+ int halfWay = TOTAL_WORK / 2;
+ int currentIncrement = 4;
+ int nextProgress = currentIncrement;
+ int worked = 0;
+
+ connection.flush();
+
+ try {
+ while (true) {
+ String response = connection.readToken();
+
+ // Update monitor work amount
+ if (--nextProgress <= 0) {
+ subMonitor.worked(1);
+ worked++;
+ if (worked >= halfWay) {
+ //we have passed the current halfway point, so double the
+ //increment and reset the halfway point.
+ currentIncrement *= 2;
+ halfWay += (TOTAL_WORK - halfWay) / 2;
+ }
+ //reset the progress counter to another full increment
+ nextProgress = currentIncrement;
+ }
+ Policy.checkCanceled(subMonitor);
+
+ // Distiguage between three different tokens:
+ // OK => break
+ // ERROR => throw error (implicit break)
+ // rest => handle it
+ if (response.startsWith(OK)) {
+ break;
+ } else if (ERROR.equals(response)) {
+ throw serverErrorConnection(connection);
+ } else {
+ handle(response,connection,messageOutput,mRoot,monitor);
+ }
+ }
+ } finally {
+ subMonitor.done();
+ }
+ }
+
+ /**
+ * Reads error-data from the server and throws an
+ * exception.
+ */
+ private static CVSException serverErrorConnection(Connection connection) {
+
+ String message = ERROR;
+
+ // The error tag can be followed by an error message too
+ if (connection.getLastUsedDelimiterToken() == IResponseHandler.BLANK_DELIMITER) {
+ try {
+ message = connection.readLine();
+ } catch (CVSException e) {
+ // We get nothing and go on sending the standard-message
+ }
+ }
+
+ if (message.equals("") || message.equals(" ")) {
+ message = Policy.bind("ResponseDispatcher.serverError");
+ }
+
+ return new CVSServerException(message);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseHandler.java
new file mode 100644
index 000000000..fe7575b82
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/ResponseHandler.java
@@ -0,0 +1,55 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * ResponseHandler is an abstract class implementing the IResponseHandler.
+ *
+ * At the moment it does just provide some additional helper-classes.
+ */
+public abstract class ResponseHandler implements IResponseHandler {
+
+ public static final String SERVER_DELIM = "/";
+ public static final String DUMMY_TIMESTAMP = "dummy timestamp";
+ public static final String RESULT_OF_MERGE = "Result of merge+";
+
+ /**
+ * Call the old method without a monitor. Either this method or
+ * the called method have to be overloaded, otherwise an
+ * UnsupportedOperationException is thrown.<br>
+ * This is done for convinience to be able to keep the old methods
+ * that do not use a progress-monitor.
+ *
+ * Handle the given response from the server.
+ */
+ public void handle(Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+
+ handle(connection,messageOutput,mRoot);
+ }
+
+ /**
+ * This method throws an UnsupportedOperationException.
+ * To be overloaded
+ */
+ public void handle(Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/StaticHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/StaticHandler.java
new file mode 100644
index 000000000..00d57c3fb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/StaticHandler.java
@@ -0,0 +1,126 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.requests.RequestSender;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * Response to the Clear-static-directory and the Set-static-directory
+ * responses of the server.
+ * Out of this responses the folder-structure is generated and the
+ * information wether the folder a static is set.
+ */
+class StaticHandler extends ResponseHandler {
+
+ public static final String SET_STATIC_RESPONSE = "Set-static-directory";
+ public static final String CLEAR_STATIC_RESPONSE = "Clear-static-directory";
+ private final boolean setStatic;
+
+ /**
+ * Constructor
+ *
+ * @param setStatic => SetStaticHandler
+ !setStatic => ClearStaticHandler
+ */
+ public StaticHandler(boolean setStatic) {
+ this.setStatic = setStatic;
+ }
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ if (setStatic) {
+ return SET_STATIC_RESPONSE;
+ } else {
+ return CLEAR_STATIC_RESPONSE;
+ }
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, PrintStream, IManagedFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot)
+ throws CVSException {
+
+ String localDirectory;
+ String remoteDirectory;
+
+ IManagedFolder mFolder;
+ FolderProperties folderInfo;
+
+ // Read the info associated with the Updated response
+ localDirectory = connection.readLine();
+ remoteDirectory = connection.readLine();
+
+ // Cut the last slash form the
+ Assert.isTrue(remoteDirectory.endsWith(SERVER_DELIM));
+ remoteDirectory = remoteDirectory.substring(0,remoteDirectory.length() -
+ SERVER_DELIM.length());
+
+ mFolder = mRoot.getFolder(localDirectory);
+ mFolder.mkdir();
+ // Make sure that we were able to create the folder
+ Assert.isTrue(mFolder.exists());
+
+ folderInfo = createFolderInfo(connection,remoteDirectory,setStatic);
+ mFolder.setFolderInfo(folderInfo);
+
+ }
+
+
+ /**
+ * Get the String identifying the repository which should be stored with
+ * all folders retrieved from the repository. This string corresponds to the
+ * identifier stored in the "Root" file by most cvs clients
+ */
+ public static String getRoot(Connection connection) {
+ return connection.getCVSRoot().getLocation();
+ }
+
+ /**
+ * Get the realative Path of the resource in comparison to
+ * the homedirectory.
+ *
+ * This is used to save it while storing the Properties
+ * for a folder (the Repository-file)
+ */
+ private static String getRepository(Connection connection, String resourceName)
+ throws CVSException {
+
+ return Util.getRelativePath(connection.getRootDirectory(),
+ resourceName);
+ }
+
+ /**
+ * Constructs a folderInfo out of the information provided
+ * from connection and the local Path of the folder.
+ *
+ * Sets all the properties of the FolderProperties.
+ */
+ private static FolderProperties createFolderInfo(Connection connection,
+ String localFolderPath,
+ boolean staticFolder)
+ throws CVSException {
+
+ return new FolderProperties(getRoot(connection),
+ getRepository(connection,localFolderPath),
+ staticFolder);
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UnsupportedHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UnsupportedHandler.java
new file mode 100644
index 000000000..da0fc3966
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UnsupportedHandler.java
@@ -0,0 +1,48 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+
+/**
+ * The UnsupportedHandler throws an error whenever
+ * it is called.
+ * It is made for a response, that we have to answer on
+ * but it has to be registerd at a later time.
+ */
+class UnsupportedHandler extends ResponseHandler {
+
+ String name;
+
+ public UnsupportedHandler(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @see IResponseHandler#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @see IResponseHandler#handle(Connection, OutputStream, ICVSFolder)
+ */
+ public void handle(
+ Connection connection,
+ PrintStream monitor,
+ IManagedFolder mRoot)
+ throws CVSException {
+ throw new CVSException(Policy.bind("UnsupportedHandler.message"));
+ }
+
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UpdateExisting.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UpdateExisting.java
new file mode 100644
index 000000000..01183dc26
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/UpdateExisting.java
@@ -0,0 +1,24 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+//class UpdateExisting extends Updated {
+//
+// /**
+// * Constructor for UpdateExisting.
+// * @param modTimeHandler
+// * @param upToDate
+// */
+// public UpdateExisting(ModTimeHandler modTimeHandler) {
+// super(modTimeHandler, true);
+// }
+//
+// public String getName() {
+// return "Update-existing";
+// }
+//
+//}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Updated.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Updated.java
new file mode 100644
index 000000000..34822f651
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/Updated.java
@@ -0,0 +1,132 @@
+package org.eclipse.team.internal.ccvs.core.response;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * Response on the "Updated" or "Merged" token form the server.
+ *
+ * Does get information about the file that is updated
+ * and the file-content itself and puts it on the fileSystem.
+ *
+ * The difference beetween the "Updated" and the "Merged" is, that
+ * an "Merged" file is not going to be up-to-date after the operation.
+ *
+ * Requiers a exisiting parent-folder.
+ */
+class Updated extends ResponseHandler {
+
+ public static final String UPDATE_NAME = "Updated";
+ public static final String MERGE_NAME = "Merged";
+
+ private static final String READ_ONLY_FLAG = "u=rw";
+
+ private final ModTimeHandler modTimeHandler;
+ private final boolean upToDate;
+
+ /**
+ * @param upToDate => Updated-Handler
+ * @param !upToDate => Merged-Handler
+ */
+ public Updated(ModTimeHandler modTimeHandler, boolean upToDate) {
+ this.modTimeHandler = modTimeHandler;
+ this.upToDate = upToDate;
+ }
+
+ public String getName() {
+ if (upToDate) {
+ return UPDATE_NAME;
+ } else {
+ return MERGE_NAME;
+ }
+ }
+
+ /**
+ * Get the realative Path of the resource in comparison to
+ * the homedirectory.
+ *
+ * This is used to save it while storing the Properties
+ * for a folder (the Repository-file)
+ */
+ public static String getRepository(Connection connection, String resourceName)
+ throws CVSException {
+
+ return Util.getRelativePath(connection.getRootDirectory(),
+ resourceName);
+ }
+
+ public void handle(Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+
+ String fileName;
+ int size;
+ boolean binary;
+ boolean readOnly;
+
+ IManagedFile mFile;
+ IManagedFolder mParent;
+ FileProperties fileInfo;
+
+ InputStream in;
+
+ // Read the info associated with the Updated response
+ String localDirectory = connection.readLine();
+ String repositoryFilename = connection.readLine();
+ String entry = connection.readLine();
+ String permissions = connection.readLine();
+
+ // Read the number of bytes in the file
+ String line = connection.readLine();
+
+ try {
+ size = Integer.parseInt(line);
+ } catch (NumberFormatException e) {
+ throw new CVSException(Policy.bind("Updated.numberFormat"));
+ }
+
+ // Get the local file
+ fileName = repositoryFilename.substring(
+ repositoryFilename.lastIndexOf(SERVER_DELIM) + 1);
+ mParent = mRoot.getFolder(localDirectory);
+ Assert.isTrue(mParent.exists() && mParent.isCVSFolder());
+ mFile = mParent.getFile(fileName);
+
+ in = connection.getResponseStream();
+
+ fileInfo = new FileProperties(entry, permissions);
+ binary = fileInfo.getKeywordMode().indexOf(FileProperties.BINARY_TAG) != -1;
+ readOnly = permissions.indexOf(READ_ONLY_FLAG) == -1;
+
+ mFile.receiveFrom(in,monitor,size,binary,readOnly);
+
+ // Set the timestamp in the file
+ // set the result in the fileInfo
+ mFile.setTimeStamp(modTimeHandler.pullLastTime());
+ if (upToDate) {
+ fileInfo.setTimeStamp(mFile.getTimeStamp());
+ } else {
+ fileInfo.setTimeStamp(RESULT_OF_MERGE + mFile.getTimeStamp());
+ }
+ mFile.setFileInfo(fileInfo);
+
+ Assert.isTrue(mFile.isDirty()!=upToDate);
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/CVSTag.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/CVSTag.java
new file mode 100644
index 000000000..26fbe7308
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/CVSTag.java
@@ -0,0 +1,37 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+import org.eclipse.team.internal.ccvs.core.response.*;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+public class CVSTag {
+ // Tag is a branch
+ public final static int BRANCH_TAG = 1;
+ // Tag is a version
+ public final static int VERSION_TAG = 2;
+
+ // Tag name
+ private String name;
+ // Tag type
+ private int type;
+
+ public CVSTag(String name, int type) {
+ this.name = name;
+ this.type = type;
+ }
+ /**
+ * Returns the tag name
+ */
+ public String getName() {
+ return name;
+ }
+ /**
+ * Returns the tag type
+ */
+ public int getType() {
+ return type;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/ILogListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/ILogListener.java
new file mode 100644
index 000000000..2de73eeca
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/ILogListener.java
@@ -0,0 +1,15 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+import org.eclipse.team.internal.ccvs.core.response.*;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+public interface ILogListener {
+ /**
+ * file status
+ */
+ public void log(String revision, String author, String date, String comment, String state, CVSTag[] tags);
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IStatusListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IStatusListener.java
new file mode 100644
index 000000000..00dd380d8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IStatusListener.java
@@ -0,0 +1,23 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.internal.ccvs.core.response.*;
+
+public interface IStatusListener {
+
+ public static final String FOLDER_RIVISION = "";
+
+ /**
+ * provides access to the revision of a file through
+ * the use of the Status command.
+ *
+ * @see StatusMessageHandler
+ * @see StatusErrorHandler
+ */
+ public void fileStatus(IPath path, String remoteRevision);
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IUpdateMessageListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IUpdateMessageListener.java
new file mode 100644
index 000000000..7261ce0b8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/IUpdateMessageListener.java
@@ -0,0 +1,29 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.*;
+
+/**
+ * This listener is used by RemoteFolder to listener for E and M messages
+ * from the CVS server in order to determine the files and folders contained in a parent folder.
+ */
+public interface IUpdateMessageListener {
+ /**
+ * information that a directory which has been reported using directoryInformation() does not exist
+ */
+ public void directoryDoesNotExist(IPath path);
+ /**
+ * directory information
+ */
+ public void directoryInformation(IPath path, boolean newDirectory);
+ /**
+ * file information
+ */
+ public void fileInformation(char type, String filename) throws CVSException;
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogEntry.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogEntry.java
new file mode 100644
index 000000000..460c47cfb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogEntry.java
@@ -0,0 +1,84 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFileRevision;
+import org.eclipse.team.ccvs.core.ILogEntry;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+
+public class LogEntry implements ILogEntry {
+
+ private RemoteFileRevision file;
+ private String author;
+ private String date;
+ private String comment;
+ private String state;
+ private CVSTag[] tags;
+
+ public LogEntry(RemoteFile file, String revision, String author, String date, String comment, String state, CVSTag[] tags) {
+ this.file = file.toRemoteFileRevision(revision);
+ this.author = author;
+ this.date = date;
+ this.comment = comment;
+ this.state = state;
+ this.tags = tags;
+ }
+
+ /**
+ * @see ILogEntry#getRevision()
+ */
+ public String getRevision() {
+ return file.getRevision();
+ }
+
+ /**
+ * @see ILogEntry#getAuthor()
+ */
+ public String getAuthor() {
+ return author;
+ }
+
+ /**
+ * @see ILogEntry#getDate()
+ */
+ public String getDate() {
+ return date;
+ }
+
+ /**
+ * @see ILogEntry#getComment()
+ */
+ public String getComment() {
+ return comment;
+ }
+
+ /**
+ * @see ILogEntry#getState()
+ */
+ public String getState() {
+ return state;
+ }
+
+ /**
+ * @see ILogEntry#getTags()
+ */
+ public String[] getTags() {
+ String[] tagNames = new String[tags.length];
+ for (int i=0;i<tags.length;i++)
+ tagNames[i] = tags[i].getName();
+ return tagNames;
+ }
+
+ /**
+ * @see ILogEntry#getRemoteFile()
+ */
+ public IRemoteFile getRemoteFile() {
+ return file;
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogHandler.java
new file mode 100644
index 000000000..735f6adff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogHandler.java
@@ -0,0 +1,177 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.ResponseHandler;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+/**
+ * Handles a valid request from the server.
+ */
+public class LogHandler extends ResponseHandler {
+
+ public static final String NAME = "M";
+
+ private ILogListener logListener;
+ private List tagNames = new ArrayList(5);
+ private List tagRevisions = new ArrayList(5);
+
+ public LogHandler(ILogListener logListener) {
+ this.logListener = logListener;
+ Assert.isNotNull(logListener);
+ }
+
+ public LogHandler(RemoteFile file, List entries) {
+ this(new LogListener(file, entries));
+ }
+
+ public String getLine(Connection context) throws CVSException {
+ String line = context.readLine();
+ if (line.startsWith(getName() + " "))
+ return line.substring(2);
+ else
+ return line;
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ /** branch tags have odd number of segments or have
+ * an even number with a zero as the second last segment
+ * e.g: 1.1.1, 1.26.0.2 are branch revision numbers */
+ protected boolean isBranchTag(String tagName) {
+ int numberOfSegments = 0;
+ boolean isBranch = false;
+ for (int i = 0; i < tagName.length(); i++) {
+ if (tagName.charAt(i) == '.')
+ numberOfSegments++;
+ }
+ isBranch = (numberOfSegments % 2) == 0;
+ if (!isBranch && tagName.lastIndexOf('0') != -1)
+ isBranch = true;
+ return isBranch;
+ }
+
+ public void handle(
+ Connection context,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+
+ String line = null;
+
+ // Fields we will find in the log for a file
+ // keys = String (tag name), values = String (tag revision number) */
+ String creationDate;
+ String author;
+ String comment;
+ String revision;
+ String state;
+
+ // get the line
+ line = context.readLine();
+
+ if (line.startsWith("symbolic names:")) {
+ line = getLine(context);
+ while (!line.startsWith("keyword substitution:")) {
+ int firstColon = line.indexOf(':');
+ String tagName = line.substring(1, firstColon);
+ String tagRevision = line.substring(firstColon + 2);
+ tagNames.add(tagName);
+ tagRevisions.add(tagRevision);
+ line = getLine(context);
+ };
+ }
+
+ // next lines will have "M " prefixed to each line, this is because the
+ // response context does not strip these. We loop so we must remove them.
+ if (line.startsWith("revision ")) {
+
+ boolean done = false;
+
+ // get the revision number
+ revision = line.substring(9);
+
+ // read next line, which looks like this:
+ // date: 2000/06/19 04:56:21; author: somebody; state: Exp; lines: +114 -45
+ line = getLine(context);
+
+ // get the creation date
+ int endOfDateIndex = line.indexOf(';', 6);
+ creationDate = line.substring(6, endOfDateIndex) + " GMT";
+
+ // get the author name
+ int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1);
+ author = line.substring(endOfDateIndex + 11, endOfAuthorIndex);
+
+ // get the file state (because this revision might be "dead")
+ state =
+ line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1));
+
+ // read comment
+ // skip next line (info about branches) if it exists, if not then it is a comment line.
+ line = getLine(context);
+ if (line.startsWith("branches:"))
+ line = getLine(context);
+ comment = "";
+ while (line != null) {
+ if (line
+ .equals("=============================================================================")
+ || line.equals("----------------------------")) {
+ done = true;
+ break;
+ }
+ if (!comment.equals(""))
+ comment += "\n";
+ comment += line;
+ line = getLine(context);
+ }
+
+ // we are only interested in tag names for this revision, remove all others.
+ List thisRevisionTags = new ArrayList(3);
+ for (int i = 0; i < tagNames.size(); i++) {
+ String tagName = (String) tagNames.get(i);
+ String tagRev = (String) tagRevisions.get(i);
+ // If this is a branch tag then only include this tag with the revision
+ // that is the root of this branch (e.g. 1.1 is root of branch 1.1.2).
+ boolean isBranch = isBranchTag(tagRev);
+ if (isBranch) {
+ int zeroIndex = tagRev.lastIndexOf('0');
+ int lastDot = -1;
+ if (zeroIndex != -1)
+ lastDot = zeroIndex - 1;
+ else
+ lastDot = tagRev.lastIndexOf('.');
+ tagRev = tagRev.substring(0, lastDot);
+ }
+ if (tagRev.equals(revision)) {
+ int type = isBranch ? CVSTag.BRANCH_TAG : CVSTag.VERSION_TAG;
+ thisRevisionTags.add(new CVSTag(tagName, type));
+ }
+ }
+ logListener.log(
+ revision,
+ author,
+ creationDate,
+ comment,
+ state,
+ (CVSTag[]) thisRevisionTags.toArray(new CVSTag[0]));
+ }
+
+ return;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogListener.java
new file mode 100644
index 000000000..f649b983b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/LogListener.java
@@ -0,0 +1,39 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.List;
+
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+
+public class LogListener implements ILogListener {
+
+ private List entries;
+ private RemoteFile file;
+ /**
+ * Constructor for LogListener.
+ */
+ public LogListener(RemoteFile file, List entires) {
+ this.entries = entries;
+ this.file = file;
+ }
+
+ /**
+ * @see ILogListener#log(String, String, String, String, String, CVSTag[])
+ */
+ public void log(
+ String revision,
+ String author,
+ String date,
+ String comment,
+ String state,
+ CVSTag[] tags) {
+
+ entries.add(new LogEntry(file, revision, author, date, comment, state, tags));
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusErrorHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusErrorHandler.java
new file mode 100644
index 000000000..05a3b2e28
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusErrorHandler.java
@@ -0,0 +1,56 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.*;
+
+public class StatusErrorHandler extends ResponseHandler {
+
+ public static final String NAME = "E";
+
+ private static boolean isFolder = false;
+ private IStatusListener statusListener;
+ private List errors;
+
+ public StatusErrorHandler(IStatusListener statusListener) {
+ this.statusListener = statusListener;
+ }
+ public String getName() {
+ return NAME;
+ }
+ public void handle(
+ Connection context,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+ String line = context.readLine();
+ if (line.startsWith("cvs server: Examining")) {
+ isFolder = true;
+ return;
+ }
+ if (isFolder && line.startsWith("cvs [server aborted]: could not chdir to")) {
+ String folderPath = line.substring(41, line.indexOf(':', 42));
+ // Pass null to listener indication that resource exists but does not a revision number
+ if (statusListener != null)
+ statusListener.fileStatus(
+ new Path(folderPath).removeFirstSegments(1),
+ IStatusListener.FOLDER_RIVISION);
+ isFolder = false;
+ return;
+ }
+ errors.add(line);
+ return;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusMessageHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusMessageHandler.java
new file mode 100644
index 000000000..f35644ca2
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/StatusMessageHandler.java
@@ -0,0 +1,76 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.*;
+
+public class StatusMessageHandler extends ResponseHandler {
+
+ /** The response key */
+ public static final String NAME = "M";
+
+ IStatusListener statusListener;
+
+ public StatusMessageHandler(IStatusListener statusListener) {
+ this.statusListener = statusListener;
+ }
+ /**
+ * @see ResponseHandler#getName()
+ */
+ public String getName() {
+ return NAME;
+ }
+ /**
+ * Handle the response. This response sends the message to the
+ * progress monitor using <code>IProgressMonitor.subTask(Strin)
+ * </code>.
+ */
+ public void handle(
+ Connection context,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor)
+ throws CVSException {
+ String line = context.readLine();
+ StringBuffer tags = new StringBuffer(10);
+ if (line.startsWith(" Repository revision:")) {
+ if (!line.startsWith(" Repository revision: No revision control file")) {
+ int separatingTabIndex = line.indexOf('\t', 24);
+ String remoteRevision = line.substring(24, separatingTabIndex);
+
+ // This is the full location on the server (e.g. /home/cvs/repo/project/file.txt)
+ String fileLocation = line.substring(separatingTabIndex + 1, line.length() - 2);
+
+ // This is the project relative path (e.g. /project/file.txt)
+ IPath fullPath =
+ new Path(fileLocation.substring(context.getRootDirectory().length() + 1));
+
+ // If the status returns that the file is in the Attic, then remove the
+ // Attic segment. This is because files added to a branch that are not in
+ // the main trunk (HEAD) are added to the Attic but cvs does magic on update
+ // to put them in the correct location.
+ // (e.g. /project/Attic/file.txt -> /project/file.txt)
+ if (fullPath.segment(fullPath.segmentCount() - 2).equals("Attic")) {
+ String filename = fullPath.lastSegment();
+ fullPath = fullPath.removeLastSegments(2);
+ fullPath = fullPath.append(filename);
+ }
+
+ // Try and get the tags from the end of the status output
+ statusListener.fileStatus(fullPath.removeFirstSegments(1), remoteRevision);
+ }
+ }
+ return;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateErrorHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateErrorHandler.java
new file mode 100644
index 000000000..2d1d2a59b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateErrorHandler.java
@@ -0,0 +1,67 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.util.List;
+
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.ResponseHandler;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.internal.ccvs.core.resources.*;
+
+/**
+ * This handler is used by the RemoteResource hierarchy to retrieve E messages
+ * from the CVS server in order to determine the folders contained in a parent folder.
+ */
+public class UpdateErrorHandler extends ResponseHandler {
+
+ public static final String NAME = "E";
+
+ IUpdateMessageListener updateMessageListener;
+ List errors;
+
+ public UpdateErrorHandler(IUpdateMessageListener updateMessageListener, List errors) {
+ this.updateMessageListener = updateMessageListener;
+ this.errors = errors;
+ }
+ public String getName() {
+ return NAME;
+ }
+ public void handle(Connection context,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor) throws CVSException {
+ String line = context.readLine();
+ if (line.startsWith("cvs server: Updating")) {
+ if (updateMessageListener != null) {
+ IPath path = new Path(line.substring(21));
+ updateMessageListener.directoryInformation(path, false);
+ }
+ } else if (line.startsWith("cvs server: skipping directory")) {
+ if (updateMessageListener != null) {
+ IPath path = new Path(line.substring(31));
+ updateMessageListener.directoryDoesNotExist(path);
+ }
+ } else if (line.startsWith("cvs server: New directory")) {
+ if (updateMessageListener != null) {
+ IPath path = new Path(line.substring(27, line.indexOf('\'', 27)));
+ updateMessageListener.directoryInformation(path, true);
+ }
+ } else if (!line.startsWith("cvs server: cannot open directory")
+ && !line.startsWith("cvs server: nothing known about")) {
+ errors.add(new Status(IStatus.ERROR, CVSProviderPlugin.ID, CVSException.IO_FAILED, line, null));
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateMessageHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateMessageHandler.java
new file mode 100644
index 000000000..05e4ee26a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/response/custom/UpdateMessageHandler.java
@@ -0,0 +1,54 @@
+package org.eclipse.team.internal.ccvs.core.response.custom;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.connection.Connection;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.response.ResponseHandler;
+import org.eclipse.team.internal.ccvs.core.resources.*;
+
+
+/**
+ * This handler is used by the RemoteResource hierarchy to retrieve M messages
+ * from the CVS server in order to determine the files contained in a parent folder.
+ */
+public class UpdateMessageHandler extends ResponseHandler {
+
+ /** The response key */
+ public static final String NAME = "M";
+
+ IUpdateMessageListener updateMessageListener;
+
+ public UpdateMessageHandler(IUpdateMessageListener updateMessageListener) {
+ this.updateMessageListener = updateMessageListener;
+ }
+ /**
+ * @see ResponseHandler#getName()
+ */
+ public String getName() {
+ return NAME;
+ }
+ /**
+ * Handle the response. This response sends the message to the
+ * progress monitor using <code>IProgressMonitor.subTask(Strin)
+ * </code>.
+ */
+ public void handle(Connection connection,
+ PrintStream messageOutput,
+ IManagedFolder mRoot,
+ IProgressMonitor monitor) throws CVSException {
+ String line = connection.readLine();
+ if (updateMessageListener == null)
+ return;
+ String path = line.substring(2);
+ updateMessageListener.fileInformation(line.charAt(0), path);
+
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Assert.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Assert.java
new file mode 100644
index 000000000..e0d437f8e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Assert.java
@@ -0,0 +1,98 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code.
+ * The predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (and certainly should not being catching them
+ * specifically).
+ * </p>
+ */
+public final class Assert {
+ /* This class is not intended to be instantiated. */
+ private Assert() {
+ }
+ /** Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ *
+ * @param expression the outcode of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+ public static boolean isLegal(boolean expression) {
+ return isLegal(expression, "");
+ }
+ /** Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcode of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+ public static boolean isLegal(boolean expression, String message) {
+ if (!expression)
+ throw new IllegalArgumentException(message);
+ return expression;
+ }
+ /** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param object the value to test
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+ public static void isNotNull(Object object) {
+ isNotNull(object, "");
+ }
+ /** Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param object the value to test
+ * @param message the message to include in the exception
+ * @exception IllegalArgumentException if the object is <code>null</code>
+ */
+ public static void isNotNull(Object object, String message) {
+ if (object == null)
+ throw new AssertionFailedException("null argument:" + message);
+ }
+ /** Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param expression the outcode of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+ public static boolean isTrue(boolean expression) {
+ return isTrue(expression, "");
+ }
+ /** Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcode of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+ public static boolean isTrue(boolean expression, String message) {
+ if (!expression)
+ throw new AssertionFailedException("assertion failed: " + message);
+ return expression;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AssertionFailedException.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AssertionFailedException.java
new file mode 100644
index 000000000..99a2892a5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/AssertionFailedException.java
@@ -0,0 +1,28 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * <code>AssertionFailedException</code> is a runtime exception thrown
+ * by some of the methods in <code>Assert</code>.
+ * <p>
+ * This class is not declared public to prevent some misuses; programs that catch
+ * or otherwise depend on assertion failures are susceptible to unexpected
+ * breakage when assertions in the code are added or removed.
+ * </p>
+ */
+/* package */
+class AssertionFailedException extends RuntimeException {
+/** Constructs a new exception.
+ */
+public AssertionFailedException() {
+}
+/** Constructs a new exception with the given message.
+ */
+public AssertionFailedException(String detail) {
+ super(detail);
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EmptyTokenizer.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EmptyTokenizer.java
new file mode 100644
index 000000000..bed051c19
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EmptyTokenizer.java
@@ -0,0 +1,132 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+
+/**
+ * The EmptyTokenizer is essentialy a StringTokenizer.
+ *
+ * The difference is that empty Tokens are given back as
+ * empty Strings.
+ *
+ * If giveFirst an emptyString is the first return of nextToken()
+ * on line that starts with its delim.
+ *
+ * If giveLast an emptyString is the last retrun of nextToken() on
+ * line that ends with its delim.
+ */
+public class EmptyTokenizer {
+
+ private StringTokenizer st;
+ private boolean lastWasDelim;
+ private final boolean giveLast;
+ private final boolean giveFirst;
+ private String delim;
+ private String line;
+
+ private String cacheToken;
+
+ public EmptyTokenizer(String line, String delim) {
+ this(line, delim, false, true);
+ }
+
+ public EmptyTokenizer(String line, String delim, boolean giveFirst, boolean giveLast) {
+
+ this.delim = delim;
+ this.line = line;
+ this.giveFirst = giveFirst;
+ lastWasDelim = giveFirst;
+ this.giveLast = giveLast;
+
+ st = new StringTokenizer(line, delim, true);
+ startCacheToken();
+
+ }
+
+ /**
+ * Call this before using cachedNextToken().
+ */
+ private void startCacheToken() {
+ if (st.hasMoreTokens()) {
+ cacheToken = st.nextToken();
+ } else {
+ cacheToken = null;
+ }
+ }
+
+ /**
+ * Gives the next Token back. Ensures that cacheToken holds the
+ * next Token to be given back.
+ * This is done to have a chance to predict if we have got a token
+ * at the end of the entry.
+ */
+ private String cachedNextToken() throws NoSuchElementException {
+
+ String oldCacheToken = cacheToken;
+
+ if (cacheToken == null) {
+ throw new NoSuchElementException();
+ }
+
+ if (st.hasMoreTokens()) {
+ cacheToken = st.nextToken();
+ } else {
+ cacheToken = null;
+ }
+
+ return oldCacheToken;
+ }
+
+
+ public boolean hasMoreTokens() {
+ return (giveLast && lastWasDelim) ||
+ (cacheToken != null && !cacheToken.equals(delim)) ||
+ (cacheToken != null && cacheToken.equals(delim) && st.hasMoreTokens()) ||
+ (cacheToken != null && cacheToken.equals(delim) && giveLast);
+ }
+
+ public String nextToken() {
+
+ String token;
+
+ if (giveLast && lastWasDelim && cacheToken == null) {
+ lastWasDelim = false;
+ return "";
+ }
+
+ token = cachedNextToken();
+
+ if (!token.equals(delim)) {
+ lastWasDelim = false;
+ return token;
+ }
+
+ if (lastWasDelim) {
+ return "";
+ } else {
+ lastWasDelim = true;
+ return nextToken();
+ }
+ }
+
+ public int countTokens() {
+
+ EmptyTokenizer tmpEmptyTokenizer;
+ int i=0;
+
+ tmpEmptyTokenizer = new EmptyTokenizer(line, delim, giveFirst, giveLast);
+ while (tmpEmptyTokenizer.hasMoreTokens()) {
+ tmpEmptyTokenizer.nextToken();
+ i++;
+ }
+
+ return i;
+
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EntriesVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EntriesVisitor.java
new file mode 100644
index 000000000..dac3ccb80
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/EntriesVisitor.java
@@ -0,0 +1,108 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFile;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedVisitor;
+
+/**
+ * Merges all the Entries.Log files with the corriponding
+ * Entries files in the CVS folders in this folder and all
+ * the subfolders (deep).<br>
+ *
+ * (Entries.Log files contain changes in the Entries files
+ * read a CVS command line client manual)
+ */
+public class EntriesVisitor implements IManagedVisitor {
+
+ public static final String ENTRIES_PROPERTY = FileProperties.ENTRIES;
+ public static final String ENTRIES_LOG_PROPERTY="Entries.Log";
+ public static final String FOLDER_TAG="D";
+ public static final String ADD_TAG="A ";
+ public static final String REMOVE_TAG="R ";
+
+ /**
+ * Constructor for EntriesVisitor.
+ */
+ public EntriesVisitor() {
+ super();
+ }
+
+ /**
+ * @see IManagedVisitor#visitFile(IManagedFile)
+ */
+ public void visitFile(IManagedFile mFile) {
+ // Do nothing ... files do not have cvs-folders in that we could
+ // possible merge
+ }
+
+ /**
+ * @see IManagedVisitor#visitFolder(IManagedFolder)
+ */
+ public void visitFolder(IManagedFolder mFolder) throws CVSException {
+
+ if (!mFolder.exists()) {
+ return;
+ }
+
+ // We still go into the subdirectories if we are not in a cvs-folder,
+ // because we might be in the root (on a checkout in a testcase).
+ //
+ // The case that we are not in a cvsFolder could be changed to an
+ // assert or even an exception. Make sure you break nothing.
+ if (mFolder.isCVSFolder()) {
+ mergeEntries(mFolder);
+ }
+
+ mFolder.acceptChildren(this);
+
+ }
+
+ /**
+ * Get an entries and an entries.log file in a cvs-folder of
+ * the cvsfolder and merge them into one new entries-file
+ */
+ public static void mergeEntries(IManagedFolder mFolder) throws CVSException {
+
+ String[] entrieLogs;
+ String[] entries;
+ ArrayList newEntries = new ArrayList();
+
+ entrieLogs = mFolder.getProperty(ENTRIES_LOG_PROPERTY);
+ entries = mFolder.getProperty(ENTRIES_PROPERTY);
+
+ if (entrieLogs == null) {
+ return;
+ }
+
+ for (int i=0; i<entries.length; i++) {
+ if (!entries[i].equals(FOLDER_TAG)) {
+ newEntries.add(entries[i]);
+ }
+ }
+
+ for (int i=0; i<entrieLogs.length; i++) {
+ if (entrieLogs[i].startsWith(ADD_TAG)) {
+ newEntries.add(entrieLogs[i].substring(ADD_TAG.length()));
+ } else if (entrieLogs[i].startsWith(REMOVE_TAG)) {
+ newEntries.remove(entrieLogs[i].substring(REMOVE_TAG.length()));
+ }
+ }
+
+ newEntries.add(FOLDER_TAG);
+
+ entries = (String[]) newEntries.toArray(new String[newEntries.size()]);
+
+ mFolder.setProperty(ENTRIES_PROPERTY,entries);
+ mFolder.setProperty(ENTRIES_LOG_PROPERTY,null);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/FileDateFormat.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/FileDateFormat.java
new file mode 100644
index 000000000..cdecc6c46
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/FileDateFormat.java
@@ -0,0 +1,41 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Does convertion beetween this timeformats:<br>
+ * <ul>
+ * <li> "Thu Oct 18 20:21:13 2001"
+ * <li> a long messuring the milliseconds after 1970
+ * </ul>
+ */
+public class FileDateFormat extends SimpleDateFormat {
+
+ public static final String FORMAT = "E MMM dd HH:mm:ss yyyy";
+
+ public FileDateFormat() {
+ super(FORMAT);
+ }
+
+ /**
+ * "Thu Oct 18 20:21:13 2001" => long since 1970
+ */
+ public long parseMill(String text) throws ParseException {
+ return parse(text).getTime();
+ }
+
+ /**
+ * long since 1970 => "Thu Oct 18 20:21:13 2001"
+ */
+ public String formatMill(long millSec) {
+ return format(new Date(millSec));
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ListFileFilter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ListFileFilter.java
new file mode 100644
index 000000000..e11a9487a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ListFileFilter.java
@@ -0,0 +1,84 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.File;
+import java.io.FileFilter;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import sun.audio.ContinuousAudioDataStream;
+
+/**
+ * This class is a default FileFilter, that is used to
+ * ex- or include certain files on a ioFile.listFiles.
+ *
+ * @see java.io.FileFilter
+ */
+public class ListFileFilter implements FileFilter {
+
+ private final String[] fileNameList;
+ private final boolean exclude;
+ private final boolean fileOnly;
+ private final boolean folderOnly;
+
+ /**
+ * @param fileNameList the list of fileNames you want to include or exclude
+ * @param exclude if true all files not in fileNameList are shown
+ if false only files in fileNameList are shown
+ */
+ public ListFileFilter(String[] fileNameList, boolean exclude) {
+ this(fileNameList,exclude,false, false);
+ }
+
+ /**
+ * @param fileNameList the list of fileNames you want to include or exclude
+ * @param exclude if true all files not in fileNameList are shown
+ if false only files in fileNameList are shown
+ * @param fileOnly if true only files are shown
+ * @param folderOnly if true only folders are shown
+ */
+ public ListFileFilter(String[] fileNameList, boolean exclude, boolean fileOnly, boolean folderOnly) {
+
+ Assert.isTrue(!fileOnly || !folderOnly);
+
+ if (fileNameList == null) {
+ fileNameList = new String[0];
+ }
+
+ this.fileNameList = fileNameList;
+ this.exclude = exclude;
+ this.fileOnly = fileOnly;
+ this.folderOnly = folderOnly;
+ }
+
+ /**
+ * @see FileFilter#accept(File)
+ */
+ public boolean accept(File file) {
+
+ boolean result = exclude;
+ String fileName = file.getName();
+
+ // If the resource is of the wrong type reject it
+ if (fileOnly && file.isDirectory()) {
+ return false;
+ }
+ if (folderOnly && file.isFile()) {
+ return false;
+ }
+
+ for (int i=0; i<fileNameList.length; i++) {
+ // use starts-with because we do not want to be
+ // messed up by an following seperator
+ if (fileName.startsWith(fileNameList[i])) {
+ result = !result;
+ break;
+ }
+ }
+
+ return result;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionContentHandler.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionContentHandler.java
new file mode 100644
index 000000000..f504f10de
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionContentHandler.java
@@ -0,0 +1,162 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.eclipse.core.resources.ICommand;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+
+// NIK: Maybe we should make the Strings constants ?
+
+public class ProjectDescriptionContentHandler implements ContentHandler {
+
+ IProjectDescription desc;
+ boolean inProjectDescription = false;
+
+ boolean inComment = false;
+ StringBuffer buffer = new StringBuffer();
+
+ boolean inBuilder = false;
+ List builders = new ArrayList();
+ ICommand currentBuilder = null;
+ Map args = new HashMap();
+
+ List natures = new ArrayList();
+
+ List references = new ArrayList();
+
+ Stack tagStack = new Stack();
+
+ ProjectDescriptionContentHandler(IProjectDescription desc) {
+ this.desc = desc;
+ }
+ /**
+ * @see ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char[] chars, int startIndex, int length)
+ throws SAXException {
+ buffer.append(chars, startIndex, length);
+ }
+ /**
+ * @see ContentHandler#endDocument()
+ */
+ public void endDocument() throws SAXException {
+ }
+ /**
+ * @see ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void endElement(String namespaceURI, String localName, String qName)
+ throws SAXException {
+ if (localName.equals("project-description") && inProjectDescription) {
+ inProjectDescription = false;
+ desc.setBuildSpec((ICommand[]) builders.toArray(new ICommand[builders.size()]));
+ desc.setNatureIds((String[]) natures.toArray(new String[natures.size()]));
+ desc.setReferencedProjects(
+ (IProject[]) references.toArray(new IProject[references.size()]));
+ } else if (localName.equals("comment") && inProjectDescription && inComment) {
+ inComment = false;
+ desc.setComment(buffer.toString());
+ } else if (localName.equals("builder") && inProjectDescription && inBuilder) {
+ inBuilder = false;
+ currentBuilder.setArguments(args);
+ if (currentBuilder.getBuilderName() != null)
+ builders.add(currentBuilder);
+ }
+ if (!localName.equals(tagStack.peek())) {
+ throw new RuntimeException(Policy.bind("ProjectDescriptionContentHandler.xml"));
+ }
+ tagStack.pop();
+ }
+ /**
+ * @see ContentHandler#endPrefixMapping(java.lang.String)
+ */
+ public void endPrefixMapping(String arg0) throws SAXException {
+ }
+ /**
+ * @see ContentHandler#ignorableWhitespace(char[], int, int)
+ */
+ public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
+ throws SAXException {
+ }
+ /**
+ * @see ContentHandler#processingInstruction(java.lang.String, java.lang.String)
+ */
+ public void processingInstruction(String arg0, String arg1)
+ throws SAXException {
+ }
+ /**
+ * @see ContentHandler#setDocumentLocator(org.xml.sax.Locator)
+ */
+ public void setDocumentLocator(Locator arg0) {
+ }
+ /**
+ * @see ContentHandler#skippedEntity(java.lang.String)
+ */
+ public void skippedEntity(String e) throws SAXException {
+ }
+ /**
+ * @see ContentHandler#startDocument()
+ */
+ public void startDocument() throws SAXException {
+ }
+ /**
+ * @see ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+ */
+ public void startElement(
+ String namespaceURI,
+ String localName,
+ String qName,
+ Attributes atts)
+ throws SAXException {
+ if (localName.equals("project-description") && !inProjectDescription) {
+ inProjectDescription = true;
+ } else if (localName.equals("comment") && inProjectDescription && !inComment) {
+ inComment = true;
+ } else if (localName.equals("builder") && inProjectDescription && !inBuilder) {
+ String builderName = atts.getValue("name");
+ if (builderName != null) {
+ inBuilder = true;
+ currentBuilder = desc.newCommand();
+ currentBuilder.setBuilderName(builderName);
+ args = new HashMap(11);
+ }
+ } else if (localName.equals("arg") && inProjectDescription && inBuilder) {
+ String argName = atts.getValue("name");
+ String argValue = atts.getValue("value");
+ if (argName != null && argValue != null)
+ args.put(argName, argValue);
+ } else if (localName.equals("nature") && inProjectDescription && !inBuilder) {
+ String natureId = atts.getValue("id");
+ if (natureId != null)
+ natures.add(natureId);
+ } else if (
+ localName.equals("reference") && inProjectDescription && !inBuilder) {
+ String projectName = atts.getValue("project-name");
+ if (projectName != null)
+ references.add(
+ ResourcesPlugin.getWorkspace().getRoot().getProject(projectName));
+ }
+ // empty buffer
+ buffer = new StringBuffer();
+ tagStack.push(localName);
+ }
+ /**
+ * @see ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
+ */
+ public void startPrefixMapping(String arg0, String arg1) throws SAXException {
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
new file mode 100644
index 000000000..cc11e4f47
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionManager.java
@@ -0,0 +1,169 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.xerces.parsers.SAXParser;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+// NIK: Maybe we should make the Strings constants ?
+
+public class ProjectDescriptionManager {
+
+ public final static IPath PROJECT_DESCRIPTION_PATH = new Path(".vcm_meta");
+ public final static boolean UPDATE_PROJECT_DESCRIPTION_ON_LOAD = true;
+
+ /*
+ * Read the projetc meta file into the provider description
+ */
+ public static void readProjectDescription(IProjectDescription desc, InputStream stream) throws IOException, CVSException {
+ SAXParser parser = new SAXParser();
+ parser.setContentHandler(new ProjectDescriptionContentHandler(desc));
+ try {
+ parser.parse(new InputSource(stream));
+ } catch (SAXException ex) {
+ throw new CVSException(IStatus.ERROR, IStatus.ERROR, Policy.bind("ProjectDescriptionManager.unableToReadDescription"), ex);
+ }
+ }
+
+ public static void writeProjectDescription(IProject project, IProgressMonitor progress) throws CVSException {
+
+ // generate the new contents of the project meta file into a string
+ ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
+ String newContents = null;
+ try {
+ IProjectDescription desc = project.getDescription();
+ ProjectDescriptionWriter.writeProjectDescription(desc, byteOutputStream);
+ byteOutputStream.close();
+ newContents = byteOutputStream.toString("UTF8");
+ } catch (IOException ex) {
+ throw CVSException.wrapException(project, Policy.bind("ProjectDescriptionManager.ioDescription"), ex);
+ } catch (CoreException ex) {
+ throw CVSException.wrapException(project, Policy.bind("ProjectDescriptionManager.coreDescription"), ex);
+ }
+
+ IFile descResource = project.getFile(PROJECT_DESCRIPTION_PATH);
+ if (descResource.exists()) {
+ // check if the existing contents are the same before rewriting
+ String oldContents = null;
+ try {
+ StringBuffer stringBuffer = new StringBuffer();
+ InputStream is = ((IFile) descResource).getContents();
+ byte[] buf = new byte[512];
+ int result = is.read(buf);
+ while (result != -1) {
+ stringBuffer.append(new String(buf, 0, result, "UTF8"));
+ result = is.read(buf);
+ }
+ oldContents = stringBuffer.toString();
+ is.close();
+ } catch (IOException ex) {
+ throw CVSException.wrapException(project, Policy.bind("ProjectDescriptionManager.ioDescription"), ex);
+ } catch (CoreException ex) {
+ throw CVSException.wrapException(project, Policy.bind("ProjectDescriptionManager.coreDescription"), ex);
+ }
+
+ if (oldContents.equals(newContents)) {
+ // the contents of the new file would be the same as the old
+ return;
+ }
+ try {
+ descResource.setContents(
+ new ByteArrayInputStream(byteOutputStream.toByteArray()),
+ false,
+ false,
+ progress);
+ } catch (CoreException ex) {
+ throw CVSException.wrapException(project, Policy.bind("ProjectDescriptionManager.coreDescription"), ex);
+ }
+ } else {
+ try {
+ descResource.create(
+ new ByteArrayInputStream(byteOutputStream.toByteArray()),
+ false,
+ progress);
+ } catch (CoreException ex) {
+ throw CVSException.wrapException(descResource, Policy.bind("ProjectDescriptionManager.coreDescription"), ex);
+ }
+
+ }
+ }
+
+ /*
+ * To be called whenever the project description file is believed to have changed by
+ * a load/loadIfIncoming operation.
+ */
+ public static void updateProjectIfNecessary(IProject project, IProgressMonitor progress) throws CoreException {
+ IFile descResource = project.getFile(PROJECT_DESCRIPTION_PATH);
+ if (descResource.exists() && UPDATE_PROJECT_DESCRIPTION_ON_LOAD) {
+ // update project description file (assuming it has changed)
+ IProjectDescription desc = project.getDescription();
+ DataInputStream is = null;
+ try {
+ is = new DataInputStream(((IFile) descResource).getContents());
+ readProjectDescription(desc, is);
+ project.setDescription(desc, progress);
+ //clearOutgoingChange(project);
+ } catch(CVSException ex) {
+ Util.logError(Policy.bind("ProjectDescriptionManager.unableToReadDescription"), ex);
+ // something went wrong, delete the project description file
+ descResource.delete(true, progress);
+ } catch (IOException ex) {
+ Util.logError(Policy.bind("ProjectDescriptionManager.unableToReadDescription"), ex);
+ // something went wrong, delete the project description file
+ descResource.delete(true, progress);
+ } catch (CoreException ex) {
+ Util.logError(Policy.bind("ProjectDescriptionManager.unableToReadDescription"), ex);
+ // something went wrong, delete the project description file
+ descResource.delete(true, progress);
+ } finally {
+ // try to close the input stream
+ if (is != null)
+ try {
+ is.close();
+ } catch (IOException ex) {
+ Util.logError(Policy.bind("ProjectDescriptionManager.ioDescription"), ex);
+ }
+ }
+ }
+ }
+
+ /*
+ * Write out the project description file.
+ *
+ * For now just do it. It would be nice to detect the proper conditions
+ *
+ */
+ public static void writeProjectDescriptionIfNecessary(CVSTeamProvider provider, IResource resource, IProgressMonitor monitor) throws CVSException {
+ if (resource.getType() == IResource.PROJECT || isProjectDescription(resource)) {
+ IProject project = resource.getProject();
+ writeProjectDescription(project, monitor);
+ }
+ }
+
+ public static boolean isProjectDescription(IResource resource) {
+ return resource.getProjectRelativePath().equals(PROJECT_DESCRIPTION_PATH);
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionWriter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionWriter.java
new file mode 100644
index 000000000..cdb940bd3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ProjectDescriptionWriter.java
@@ -0,0 +1,101 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import java.util.*;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.IProjectDescription;
+import java.io.*;
+
+// NIK: Maybe we should make the Strings constants ?
+
+public class ProjectDescriptionWriter {
+ private static void appendEscapedChar(StringBuffer buffer, char c) {
+ String replacement = getReplacement(c);
+ if (replacement != null) {
+ buffer.append('&');
+ buffer.append(replacement);
+ buffer.append(';');
+ } else {
+ if ((c >= ' ' && c <= 0x7E) || c == '\n' || c == '\r' || c == '\t') {
+ buffer.append(c);
+ } else {
+ buffer.append("&#");
+ buffer.append(Integer.toString(c));
+ buffer.append(';');
+ }
+ }
+ }
+ public static String getEscaped(String s) {
+ StringBuffer result = new StringBuffer(s.length() + 10);
+ for (int i = 0; i < s.length(); ++i)
+ appendEscapedChar(result, s.charAt(i));
+ return result.toString();
+ }
+ private static String getReplacement(char c) {
+ // Encode special XML characters into the equivalent character references.
+ // These five are defined by default for all XML documents.
+ switch (c) {
+ case '<' :
+ return "lt";
+ case '>' :
+ return "gt";
+ case '"' :
+ return "quot";
+ case '\'' :
+ return "apos";
+ case '&' :
+ return "amp";
+ }
+ return null;
+ }
+ public static void writeProjectDescription(
+ IProjectDescription desc,
+ OutputStream os)
+ throws IOException {
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF8"));
+ writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ writer.println("<project-description>");
+
+ String comment = desc.getComment();
+ if (comment != null) {
+ writer.print("\t<comment>");
+ writer.print(getEscaped(desc.getComment()));
+ writer.println("</comment>");
+ }
+
+ String[] natures = desc.getNatureIds();
+ for (int i = 0; i < natures.length; i++) {
+ writer.println("\t<nature id=\"" + getEscaped(natures[i]) + "\"/>");
+ }
+
+ IProject[] references = desc.getReferencedProjects();
+ for (int i = 0; i < references.length; i++) {
+ writer.println(
+ "\t<reference project-name=\"" + getEscaped(references[i].getName()) + "\"/>");
+ }
+
+ ICommand[] commands = desc.getBuildSpec();
+ for (int i = 0; i < commands.length; i++) {
+ writer.println(
+ "\t<builder name=\"" + getEscaped(commands[i].getBuilderName()) + "\">");
+ Map args = commands[i].getArguments();
+ for (Iterator it = args.keySet().iterator(); it.hasNext();) {
+ String argName = (String) it.next();
+ String argValue = (String) args.get(argName);
+ writer.println(
+ "\t\t<arg name=\""
+ + getEscaped(argName)
+ + "\" value=\""
+ + getEscaped(argValue)
+ + "\"/>");
+ }
+ writer.println("\t</builder>");
+ }
+
+ writer.println("</project-description>");
+ writer.flush();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java
new file mode 100644
index 000000000..b0900ceff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ResourceDeltaVisitor.java
@@ -0,0 +1,144 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+
+public class ResourceDeltaVisitor implements IResourceDeltaVisitor {
+
+ private static IResourceChangeListener listener;
+ private static ResourceDeltaVisitor visitor;
+
+ private Map removals;
+ private Map additions;
+
+ public static ResourceDeltaVisitor register() {
+ if (visitor == null)
+ visitor = new ResourceDeltaVisitor();
+ if (listener == null)
+ listener = new IResourceChangeListener() {
+ public void resourceChanged(IResourceChangeEvent event) {
+ try {
+ IResourceDelta root = event.getDelta();
+ IResourceDelta[] projectDeltas = root.getAffectedChildren();
+ for (int i = 0; i < projectDeltas.length; i++) {
+ IResourceDelta delta = projectDeltas[i];
+ IResource resource = delta.getResource();
+ ITeamProvider provider = TeamPlugin.getManager().getProvider(resource);
+ if (provider instanceof CVSTeamProvider)
+ delta.accept(visitor);
+ }
+ visitor.handleChanges();
+ } catch (CoreException e) {
+ Util.logError(Policy.bind("ResourceDeltaVisitor.visitError"), e);
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.POST_CHANGE);
+ return visitor;
+ }
+
+ public static void deregister() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener);
+ }
+
+ public ResourceDeltaVisitor() {
+ this.additions = new HashMap();
+ this.removals = new HashMap();
+ }
+
+ /**
+ * @see IResourceDeltaVisitor#visit(IResourceDelta)
+ */
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ IResource resource = delta.getResource();
+ IProject project = resource.getProject();
+ switch (delta.getKind()) {
+ case IResourceDelta.ADDED :
+ addAddition(project, resource);
+ break;
+ case IResourceDelta.REMOVED :
+ // Only record files as there's nothing we can do about folders
+ addRemoval(project, resource);
+ break;
+ case IResourceDelta.CHANGED :
+ if ((delta.getFlags() & IResourceDelta.MOVED_TO) > 0)
+ addAddition(project, project.getFile(delta.getMovedToPath()));
+ if ((delta.getFlags() & IResourceDelta.MOVED_FROM) > 0)
+ addRemoval(project, project.getFile(delta.getMovedFromPath()));
+ break;
+ }
+ return true;
+ }
+
+ private void addAddition(IProject project, IResource resource) {
+ List additions = (List)this.additions.get(project);
+ if (additions == null) {
+ additions = new ArrayList();
+ this.additions.put(project, additions);
+ }
+ additions.add(resource);
+ }
+
+ private void addRemoval(IProject project, IResource resource) {
+ // Only record files as there's nothing we can do about folders
+ if (resource.getType() != IResource.FILE)
+ return;
+ List removals = (List)this.removals.get(project);
+ if (removals == null) {
+ removals = new ArrayList();
+ this.removals.put(project, removals);
+ }
+ removals.add(resource);
+ }
+
+ private void handleChanges() {
+ // Start by printing out the changes
+ //System.out.println("Resources added");
+ Iterator i = additions.keySet().iterator();
+ while (i.hasNext()) {
+ IProject project = (IProject)i.next();
+ ITeamProvider provider = TeamPlugin.getManager().getProvider(project);
+ Iterator j = ((List)additions.get(project)).iterator();
+ while (j.hasNext()) {
+ IResource resource = (IResource)j.next();
+ //System.out.println(resource.toString());
+ }
+ }
+ additions.clear();
+
+ //System.out.println("Resources removed");
+ i = removals.keySet().iterator();
+ while (i.hasNext()) {
+ IProject project = (IProject)i.next();
+ ITeamProvider provider = TeamPlugin.getManager().getProvider(project);
+ Iterator j = ((List)removals.get(project)).iterator();
+ while (j.hasNext()) {
+ IResource resource = (IResource)j.next();
+ //System.out.println(resource.toString());
+ }
+ }
+ removals.clear();
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ServerDateFormat.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ServerDateFormat.java
new file mode 100644
index 000000000..a5f7afce6
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/ServerDateFormat.java
@@ -0,0 +1,49 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Does convertion beetween this timeformats:<br>
+ * <ul>
+ * <li> "18 Oct 2001 20:21:13 -0350"
+ * <li> a long messuring the milliseconds after 1970
+ * </ul>
+ */
+public class ServerDateFormat extends SimpleDateFormat {
+
+ public static final String FORMAT = "dd MMM yyyy HH:mm:ss";
+
+ public ServerDateFormat() {
+ super(FORMAT);
+ }
+
+ /**
+ * "18 Oct 2001 20:21:13 -0350" => long since 1970
+ */
+ public long parseMill(String text) throws ParseException {
+
+ // FIXME this cuts the timezone which we do not
+ // want
+ if (text.indexOf("-") != -1) {
+ text = text.substring(0,text.indexOf("-"));
+ }
+
+ return parse(text).getTime();
+ }
+
+ /**
+ * long since 1970 => "18 Oct 2001 20:21:13 -0350"
+ */
+ public String formatMill(long millSec) {
+ return format(new Date(millSec));
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/StringMatcher.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/StringMatcher.java
new file mode 100644
index 000000000..3af537687
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/StringMatcher.java
@@ -0,0 +1,385 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.*;
+
+/**
+ * A string pattern matcher, suppporting * and ? wildcards.
+ * Note: code copied from org.eclipse.jdt.internal.core.util.StringMatcher on April 3, 2001
+ * (version 0.1 - 010901H18 [rename jbl]).
+ */
+public class StringMatcher {
+ protected String fPattern;
+ protected int fLength; // pattern length
+ protected boolean fIgnoreWildCards;
+ protected boolean fIgnoreCase;
+ protected boolean fHasLeadingStar;
+ protected boolean fHasTrailingStar;
+ protected String fSegments[]; //the given pattern is split into * separated segments
+
+ /* boundary value beyond which we don't need to search in the text */
+ protected int fBound = 0;
+
+
+ protected static final char fSingleWildCard = '\u0000';
+
+ public static class Position {
+ int start; //inclusive
+ int end; //exclusive
+ public Position(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+ public int getStart() {
+ return start;
+ }
+ public int getEnd() {
+ return end;
+ }
+ }
+ /**
+ * Find the first occurrence of the pattern between <code>start</code)(inclusive)
+ * and <code>end</code>(exclusive).
+ * @param <code>text</code>, the String object to search in
+ * @param <code>start</code>, the starting index of the search range, inclusive
+ * @param <code>end</code>, the ending index of the search range, exclusive
+ * @return an <code>StringMatcher.Position</code> object that keeps the starting
+ * (inclusive) and ending positions (exclusive) of the first occurrence of the
+ * pattern in the specified range of the text; return null if not found or subtext
+ * is empty (start==end). A pair of zeros is returned if pattern is empty string
+ * Note that for pattern like "*abc*" with leading and trailing stars, position of "abc"
+ * is returned. For a pattern like"*??*" in text "abcdf", (1,3) is returned
+ */
+
+ public StringMatcher.Position find(String text, int start, int end) {
+ if (fPattern == null|| text == null)
+ throw new IllegalArgumentException();
+
+ int tlen = text.length();
+ if (start < 0)
+ start = 0;
+ if (end > tlen)
+ end = tlen;
+ if (end < 0 ||start >= end )
+ return null;
+ if (fLength == 0)
+ return new Position(start, start);
+ if (fIgnoreWildCards) {
+ int x = posIn(text, start, end);
+ if (x < 0)
+ return null;
+ return new Position(x, x+fLength);
+ }
+
+ int segCount = fSegments.length;
+ if (segCount == 0)//pattern contains only '*'(s)
+ return new Position (start, end);
+
+ int curPos = start;
+ int matchStart = -1;
+ for (int i = 0; i < segCount && curPos < end; ++i) {
+ String current = fSegments[i];
+ int nextMatch = regExpPosIn(text, curPos, end, current);
+ if (nextMatch < 0 )
+ return null;
+ if(i == 0)
+ matchStart = nextMatch;
+ curPos = nextMatch + current.length();
+ }
+ return new Position(matchStart, curPos);
+ }
+ /**
+ * StringMatcher constructor takes in a String object that is a simple
+ * pattern which may contain *  for 0 and many characters and
+ * ?  for exactly one character.
+ *
+ * Literal '*' and '?' characters must be escaped in the pattern
+ * e.g., "\*" means literal "*", etc.
+ *
+ * Escaping any other character (including the escape character itself),
+ * just results in that character in the pattern.
+ * e.g., "\a" means "a" and "\\" means "\"
+ *
+ * If invoking the StringMatcher with string literals in Java, don't forget
+ * escape characters are represented by "\\".
+ *
+ * @param aPattern the pattern to match text against
+ * @param ignoreCase if true, case is ignored
+ * @param ignoreWildCards if true, wild cards and their escape sequences are ignored
+ * (everything is taken literally).
+ */
+ public StringMatcher(String aPattern, boolean ignoreCase, boolean ignoreWildCards) {
+ fIgnoreCase = ignoreCase;
+ fIgnoreWildCards = ignoreWildCards;
+ fLength = aPattern.length();
+
+ /* convert case */
+ if (fIgnoreCase) {
+ fPattern = aPattern.toUpperCase();
+ } else {
+ fPattern = aPattern;
+ }
+
+ if (fIgnoreWildCards) {
+ parseNoWildCards();
+ } else {
+ parseWildCards();
+ }
+ }
+ /**
+ * Given the starting (inclusive) and the ending (exclusive) poisitions in the
+ * <code>text</code>, determine if the given substring matches with aPattern
+ * @return true if the specified portion of the text matches the pattern
+ * @param String <code>text</code>, a String object that contains the substring to match
+ * @param int <code>start<code> marks the starting position (inclusive) of the substring
+ * @param int <code>end<code> marks the ending index (exclusive) of the substring
+ */
+ public boolean match(String text, int start, int end) {
+ if (null == fPattern || null == text)
+ throw new IllegalArgumentException();
+
+ if (start >= end)
+ return false;
+
+ if (fIgnoreWildCards)
+ return fPattern.regionMatches(fIgnoreCase, 0, text, start, fLength);
+ int segCount = fSegments.length;
+ if (segCount == 0)//pattern contains only '*'(s) or empty pattern
+ return true;
+ if (start == end)
+ return fLength == 0;
+ if (fLength == 0)
+ return start == end;
+
+ int tlen = text.length();
+ if (start < 0)
+ start = 0;
+ if (end > tlen)
+ end = tlen;
+
+ int tCurPos = start;
+ int bound = end - fBound;
+ if ( bound < 0)
+ return false;
+ int i=0;
+ String current = fSegments[i];
+ int segLength = current.length();
+
+ /* process first segment */
+ if (!fHasLeadingStar){
+ if(!regExpRegionMatches(text, start, current, 0, segLength)) {
+ return false;
+ } else {
+ ++i;
+ tCurPos = tCurPos + segLength;
+ }
+ }
+
+ /* process middle segments */
+ for ( ; i < segCount && tCurPos <= bound; ++i) {
+ current = fSegments[i];
+ int currentMatch;
+ int k = current.indexOf(fSingleWildCard);
+ if (k < 0) {
+ currentMatch = textPosIn(text, tCurPos, end, current);
+ if (currentMatch < 0)
+ return false;
+ } else {
+ currentMatch = regExpPosIn(text, tCurPos, end, current);
+ if (currentMatch < 0)
+ return false;
+ }
+ tCurPos = currentMatch + current.length();
+ }
+
+ /* process final segment */
+ if (!fHasTrailingStar && tCurPos != end) {
+ int clen = current.length();
+ return regExpRegionMatches(text, end - clen, current, 0, clen);
+ }
+ return i == segCount ;
+ }
+ /**
+ * match the given <code>text</code> with the pattern
+ * @return true if matched eitherwise false
+ * @param <code>text</code>, a String object
+ */
+ public boolean match(String text) {
+ return match(text, 0, text.length());
+ }
+ /**
+ * This method parses the given pattern into segments seperated by wildcard '*' characters.
+ * Since wildcards are not being used in this case, the pattern consists of a single segment.
+ */
+ private void parseNoWildCards() {
+ fSegments = new String[1];
+ fSegments[0] = fPattern;
+ fBound = fLength;
+ }
+ /**
+ * This method parses the given pattern into segments seperated by wildcard '*' characters.
+ * @param p, a String object that is a simple regular expression with *  and/or ? 
+ */
+ private void parseWildCards() {
+ if(fPattern.startsWith("*"))
+ fHasLeadingStar = true;
+ if(fPattern.endsWith("*")) {
+ /* make sure it's not an escaped wildcard */
+ if (fLength > 1 && fPattern.charAt(fLength - 2) != '\\') {
+ fHasTrailingStar = true;
+ }
+ }
+
+ Vector temp = new Vector();
+
+ int pos = 0;
+ StringBuffer buf = new StringBuffer();
+ while (pos < fLength) {
+ char c = fPattern.charAt(pos++);
+ switch (c) {
+ case '\\':
+ if (pos >= fLength) {
+ buf.append(c);
+ } else {
+ char next = fPattern.charAt(pos++);
+ /* if it's an escape sequence */
+ if (next == '*' || next == '?' || next == '\\') {
+ buf.append(next);
+ } else {
+ /* not an escape sequence, just insert literally */
+ buf.append(c);
+ buf.append(next);
+ }
+ }
+ break;
+ case '*':
+ if (buf.length() > 0) {
+ /* new segment */
+ temp.addElement(buf.toString());
+ fBound += buf.length();
+ buf.setLength(0);
+ }
+ break;
+ case '?':
+ /* append special character representing single match wildcard */
+ buf.append(fSingleWildCard);
+ break;
+ default:
+ buf.append(c);
+ }
+ }
+
+ /* add last buffer to segment list */
+ if (buf.length() > 0) {
+ temp.addElement(buf.toString());
+ fBound += buf.length();
+ }
+
+ fSegments = new String[temp.size()];
+ temp.copyInto(fSegments);
+ }
+ /**
+ * @param <code>text</code>, a string which contains no wildcard
+ * @param <code>start</code>, the starting index in the text for search, inclusive
+ * @param <code>end</code>, the stopping point of search, exclusive
+ * @return the starting index in the text of the pattern , or -1 if not found
+ */
+ protected int posIn(String text, int start, int end) {//no wild card in pattern
+ int max = end - fLength;
+
+ if (!fIgnoreCase) {
+ int i = text.indexOf(fPattern, start);
+ if (i == -1 || i > max)
+ return -1;
+ return i;
+ }
+
+ for (int i = start; i <= max; ++i) {
+ if (text.regionMatches(true, i, fPattern, 0, fLength))
+ return i;
+ }
+
+ return -1;
+ }
+ /**
+ * @param <code>text</code>, a simple regular expression that may only contain '?'(s)
+ * @param <code>start</code>, the starting index in the text for search, inclusive
+ * @param <code>end</code>, the stopping point of search, exclusive
+ * @param <code>p</code>, a simple regular expression that may contains '?'
+ * @param <code>caseIgnored</code>, wether the pattern is not casesensitive
+ * @return the starting index in the text of the pattern , or -1 if not found
+ */
+ protected int regExpPosIn(String text, int start, int end, String p) {
+ int plen = p.length();
+
+ int max = end - plen;
+ for (int i = start; i <= max; ++i) {
+ if (regExpRegionMatches(text, i, p, 0, plen))
+ return i;
+ }
+ return -1;
+ }
+ /**
+ *
+ * @return boolean
+ * @param <code>text</code>, a String to match
+ * @param <code>start</code>, int that indicates the starting index of match, inclusive
+ * @param <code>end</code> int that indicates the ending index of match, exclusive
+ * @param <code>p</code>, String, String, a simple regular expression that may contain '?'
+ * @param <code>ignoreCase</code>, boolean indicating wether code>p</code> is case sensitive
+ */
+ protected boolean regExpRegionMatches(String text, int tStart, String p, int pStart, int plen) {
+ while (plen-- > 0) {
+ char tchar = text.charAt(tStart++);
+ char pchar = p.charAt(pStart++);
+
+ /* process wild cards */
+ if (!fIgnoreWildCards) {
+ /* skip single wild cards */
+ if (pchar == fSingleWildCard) {
+ continue;
+ }
+ }
+ if (pchar == tchar)
+ continue;
+ if (fIgnoreCase) {
+ char tc = Character.toUpperCase(tchar);
+ if (tc == pchar)
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+ /**
+ * @param <code>text</code>, the string to match
+ * @param <code>start</code>, the starting index in the text for search, inclusive
+ * @param <code>end</code>, the stopping point of search, exclusive
+ * @param code>p</code>, a string that has no wildcard
+ * @param <code>ignoreCase</code>, boolean indicating wether code>p</code> is case sensitive
+ * @return the starting index in the text of the pattern , or -1 if not found
+ */
+ protected int textPosIn(String text, int start, int end, String p) {
+
+ int plen = p.length();
+ int max = end - plen;
+
+ if (!fIgnoreCase) {
+ int i = text.indexOf(p, start);
+ if (i == -1 || i > max)
+ return -1;
+ return i;
+ }
+
+ for (int i = 0; i <= max; ++i) {
+ if (text.regionMatches(true, i, p, 0, plen))
+ return i;
+ }
+
+ return -1;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Util.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Util.java
new file mode 100644
index 000000000..f6d260d3b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/Util.java
@@ -0,0 +1,342 @@
+package org.eclipse.team.internal.ccvs.core.util;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.ccvs.core.CVSProviderPlugin;
+
+/**
+ * Unsorted static helper-methods
+ */
+public class Util {
+
+ private static final String AT = "@";
+ private static final String COLON = ":";
+
+
+ // private static final String newLine = System.getProperty("line.separator");
+
+ /**
+ * Looks for search in original. If it findes search it puts excange
+ * in the result. Otherwise it copies original in the result.
+ */
+ public static byte[] replace(final byte[] original,final byte[] search,final byte[] excange) {
+
+ final int tmpMulti;
+ final byte[] tmpResult;
+ final byte[] result;
+
+ int size=0;
+ boolean replaced;
+
+ // create an array, that can hold the result for sure
+ tmpMulti = (int) Math.ceil((double)excange.length/(double)search.length);
+
+ Assert.isTrue((long)tmpMulti * (long)original.length < Integer.MAX_VALUE);
+ tmpResult = new byte[tmpMulti * original.length];
+
+ int i=0;
+ while (i < original.length) {
+ replaced = false;
+ for (int j = 0; j < search.length; j++) {
+
+ if ((i+j >= original.length) || (original[i+j] != search[j])) {
+ // Send another letter to the result
+ // array
+ break;
+ }
+
+ if (j == search.length-1) {
+ // We have found the search and going to
+ // put excange intead of it
+ System.arraycopy(excange,0,tmpResult,size,excange.length);
+ size += excange.length;
+ i += search.length;
+ replaced = true;
+ }
+ }
+
+ if (!replaced) {
+ tmpResult[size] = original[i];
+ i++;
+ size++;
+ }
+ }
+
+ result = new byte[size];
+ System.arraycopy(tmpResult,0,result,0,size);
+ return result;
+ }
+
+ /**
+ * @see Util#getOptions(String[],String,boolean)
+ */
+ public static String getOption(String[] options, String key, boolean deleteOption) {
+
+ String[] result;
+
+ result = getOptions(options,key,deleteOption);
+
+ if (result.length == 0) {
+ return null;
+ } else {
+ return result[0];
+ }
+ }
+
+ /**
+ * Get an option out of an array of options. It assumes, that
+ * the next field to the key contains the parameter to the
+ * option.
+ *
+ * @param options not null
+ * @param key not null
+ * @param deleteOption nulls both the option-tag and the information
+ * @return String[0] if the option could not be found
+ */
+ public static String[] getOptions(String[] options, String key, boolean deleteOption) {
+
+ String[] tmpResult;
+ String[] result;
+ int size = 0;
+
+ Assert.isNotNull(options);
+ Assert.isNotNull(key);
+
+ tmpResult = new String[options.length];
+
+ for (int i=0; i<options.length; i++) {
+ if (key.equals(options[i]) && i<options.length-1) {
+ tmpResult[size++] = options[i+1];
+
+ // This should be done in another way maybe we should
+ // have an options Object or give the array modified
+ // back.
+ // Maybe we are going to change that.
+ if (deleteOption) {
+ options[i] = null;
+ options[i+1] = null;
+ }
+ }
+ }
+
+ result = new String[size];
+ System.arraycopy(tmpResult,0,result,0,size);
+ return result;
+ }
+
+ /**
+ * Checks wether the Array options contains the String
+ * key.
+ * @param options not null
+ * @param key not null
+ */
+ public static boolean isOption(String[] options, String key) {
+
+ Assert.isNotNull(options);
+ Assert.isNotNull(key);
+
+ for (int i=0; i<options.length; i++) {
+ if (key.equals(options[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove or get the password out of a repoString.
+ */
+ // FIXME: This is only used for tests ... move it
+ private static String passwordHandle(String repoName, boolean remove) {
+
+ int atPlace = -1;
+ int colonPlace = -1;
+ int colonCount = 0;
+ String currentChar;
+
+ Assert.isTrue(repoName.indexOf(AT) != -1);
+ Assert.isTrue(repoName.indexOf(COLON) != -1);
+
+ for (int i=0; i<repoName.length(); i++) {
+
+ currentChar = repoName.substring(i,i+1);
+
+ if (currentChar.equals(COLON)) {
+ colonCount++;
+
+ if (colonCount == 3) {
+ colonPlace = i;
+ }
+ }
+
+ if (currentChar.equals(AT)) {
+ if (colonPlace == -1) {
+
+ // If the @ comes before the third colon, then
+ // we do not have a password and return with the
+ // same string
+ return repoName;
+ } else {
+ atPlace = i;
+ }
+
+ }
+ }
+
+ if (atPlace == -1) {
+ return repoName;
+ }
+
+ if (remove) {
+ return repoName.substring(0,colonPlace) + repoName.substring(atPlace);
+ } else {
+ return repoName.substring(colonPlace + 1, atPlace);
+ }
+ }
+
+ /**
+ * returns ":pserver:nkrambro@fiji:/home/nkrambro/repo"
+ * when you insert ":pserver:nkrambro:password@fiji:/home/nkrambro/repo"
+ */
+ // FIXME: This is only used for tests ... move it
+ public static String removePassword(String root) {
+ return passwordHandle(root,true);
+ }
+
+ /**
+ *
+ * returns "password"
+ * when you insert ":pserver:nkrambro:password@fiji:/home/nkrambro/repo"
+ */
+ // FIXME: This is only used for tests ... move it
+ public static String getPassword(String root) {
+ return passwordHandle(root,false);
+ }
+
+ // FIXME: This is only used for tests ... move it
+ public static String mergeRoot(String rootWithoutPwd, String password) {
+
+ StringBuffer result = new StringBuffer();
+
+ Assert.isTrue(rootWithoutPwd.indexOf(AT) != -1);
+ Assert.isTrue(rootWithoutPwd.indexOf(COLON) != -1);
+
+ if (password == null) {
+ return rootWithoutPwd;
+ }
+
+ result.append(rootWithoutPwd.substring(0,rootWithoutPwd.indexOf(AT)));
+ result.append(COLON);
+ result.append(password);
+ result.append(rootWithoutPwd.substring(rootWithoutPwd.indexOf(AT)));
+
+ return result.toString();
+ }
+
+// /**
+// * not used
+// */
+// public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+// if (monitor == null)
+// return new NullProgressMonitor();
+// return monitor;
+// }
+//
+// /**
+// * not used
+// */
+// public static IProgressMonitor subMonitorFor(
+// IProgressMonitor monitor,
+// int ticks,
+// int style) {
+// if (monitor == null)
+// return new NullProgressMonitor();
+// if (monitor instanceof NullProgressMonitor)
+// return monitor;
+// return new SubProgressMonitor(monitor, ticks, style);
+// }
+
+
+ /**
+ * Get the extention of the path of resource
+ * relative to the path of root
+ *
+ * @throws CVSException if root is not a root-folder of resource
+ */
+ public static String getRelativePath(String rootName, String resourceName)
+ throws CVSException {
+
+ if (!resourceName.startsWith(rootName)) {
+ throw new CVSException("Internal error, resource does not start with root.");
+ }
+
+ // Otherwise we would get an ArrayOutOfBoundException
+ // in case of two equal Resources
+ if (rootName.length() == resourceName.length()) {
+ return "";
+ }
+
+ // Get rid of the seperator, that would be in the
+ // beginning, if we did not go from +1
+ return resourceName.substring(rootName.length() + 1);
+ }
+
+ /*
+ /**
+ * Transfer an InputStream to an OutputStream
+ * and update the monitor in between.
+ *
+ * Used for saving files from server
+ * on disc, etc.
+ *
+ public static void transferWithProgress(
+ InputStream in,
+ OutputStream out,
+ long size,
+ IProgressMonitor monitor,
+ String title)
+ throws IOException {
+ // This special transfer utility will show progress to
+ // the monitor for files that are bigger than 25K
+ boolean progress = size > 25000;
+ int read = 0;
+ long totalRead = 0;
+ long ksize = size / 1024;
+ // buffer size is smaller than MAXINT...
+ int toRead = (int) Math.min(BUFFER.length, size);
+ synchronized (BUFFER) {
+ while ((totalRead < size) && (read = in.read(BUFFER, 0, toRead)) != -1) {
+ if (progress && totalRead > 0) {
+ monitor.subTask(
+ CVSPlugin.getResourceString(
+ "StreamUtil.monitor",
+ new Object[] { title, new Long(totalRead / 1024), new Long(ksize)}));
+ monitor.worked(read);
+ }
+ totalRead += read;
+ out.write(BUFFER, 0, read);
+ toRead = (int) Math.min(BUFFER.length, size - totalRead);
+ }
+ }
+ }
+ */
+ /*
+ public static void convertNewlines(IManagedFile mFile) throws CVSException {
+ mFile.setContent(mFile.getContent());
+ }
+ */
+
+ public static void logError(String message, Throwable throwable) {
+ CVSProviderPlugin.log(new Status(IStatus.ERROR, null, IStatus.ERROR, message, throwable));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/.classpath b/bundles/org.eclipse.team.cvs.ssh/.classpath
new file mode 100644
index 000000000..1fea39686
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/.classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtime.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtimesrc.zip"/>
+ <classpathentry kind="src" path="/org.eclipse.team.core"/>
+ <classpathentry kind="src" path="/org.eclipse.team.cvs.core"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.team.cvs.ssh/.cvsignore b/bundles/org.eclipse.team.cvs.ssh/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/.vcm_meta b/bundles/org.eclipse.team.cvs.ssh/.vcm_meta
new file mode 100644
index 000000000..2aa64891e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/.vcm_meta
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <reference project-name="org.eclipse.team.cvs.core"/>
+ <reference project-name="org.eclipse.team.core"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.team.cvs.ssh/about.html b/bundles/org.eclipse.team.cvs.ssh/about.html
index 3d1d90f34..652c3d8f4 100644
--- a/bundles/org.eclipse.team.cvs.ssh/about.html
+++ b/bundles/org.eclipse.team.cvs.ssh/about.html
@@ -21,14 +21,14 @@ body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( );
</tr>
<tr>
<td>
-<p>1st November, 2001</p>
+<p>22nd November, 2001</p>
<h3>License</h3>
<p>Eclipse.org makes available all content in this plug-in. Unless otherwise indicated below, the plug-in is provided to you under the terms and conditions of the
<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a>. For purposes of the Common Public License, &quot;Program&quot; will mean the plug-in.</p>
<h3>Third Party Code</h3>
-<p><b>The Java Ssh Applet</b></p>
+<p><b>The Java™ Ssh Applet</b></p>
<p>The plug-in is based on software developed by Cedric Gourio.
-If the source code is included, it is located in vcmcvssshsrc.zip. The binary code is located in vcmcvsssh.jar. The Java Ssh Applet is:</p>
+If the source code is included, it is located in teamcvssshsrc.zip. The binary code is located in teamcvsssh.jar. The Java Ssh Applet is:</p>
<blockquote>
Copyright © 1998 Cedric Gourio<br>
(<a href="http://www.cl.cam.ac.uk/~fapp2/software/java-ssh/">http://www.cl.cam.ac.uk/~fapp2/software/java-ssh/</a>)<br>
@@ -39,16 +39,42 @@ If the source code is included, it is located in vcmcvssshsrc.zip. The binary c
<p>The plug-in is based on an algorithm developed by Bruce Schneier. The algorithm was originally published in
a paper that is located at <a href="http://www.counterpane.com/bfsverlag.html">http://www.counterpane.com/bfsverlag.html</a>.
Your use of the Blowfish Encryption Algorithm in both source and binary code form contained in the plug-in is not subject to any
-additional terms and conditions. If the source code, Blowfish.java is included, it is located in vcmcvssshsrc.zip. The binary code, Blowfish.class is located in
-vcmcvsssh.jar.</p>
-<h3>Encryption Items</h3>
-<p>The plug-in contains encryption software. The country in which you are currently may have restrictions on
-the import, possession, and use, and/or re-export to another country, of
-encryption software. BEFORE using any encryption software, please
-check the country's laws, regulations and policies concerning the import,
-possession, or use, and re-export of encryption software, to see if this
+additional terms and conditions. If the source code, Blowfish.java is included, it is located in teamcvssshsrc.zip. The binary code, Blowfish.class is located in
+teamcvsssh.jar.</p>
+
+<h3>Cryptography</h3>
+
+<p><b>Export Control</b></p>
+<p>The plug-in contains encryption software. The country in which you are currently may have restrictions on the import, possession,
+and use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check the country's
+laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this
is permitted.</p>
+<p><b>Encryption Items</b></p>
+
+<p>Details of the encryption software in this plug-in are provided to assist in obtaining relevant export control classification and approval.
+Cryptography in this plug-in has been classified as Export Commodity Control Number (ECCN) 5D002.c.1 by the U.S. Government Department of Commerce,
+Bureau of Export Administration and deemed eligible for export under License Exception ENC Technology Software Unrestricted (TSU) for object code
+and (cryptographic) source code, and License Exception ENC Retail for object code and no (cryptographic) source code.</p>
+
+<p>The plug-in contains an implementation of the Secure Shell (SSH) protocol that encrypts the network traffic (program code files
+in file versioning systems) between a Concurrent Versions System (CVS) server and a CVS client. Data is encrypted using the Blowfish algorithm
+which is a 64-bit symmetric cipher that uses a key varying in length from 32 bits to 448 bits. The RSA PKCS #1 v1.5 key exchange algorithm is
+used to authenticate and exchange keys with a minimum modulus size of 96 bits and a maximum modulus size theoretically unlimited since the
+implementation uses the java.math.BigInteger class which has a maximum positive integer range limited only by the available memory in the computer.
+In practice, the key length is defined by the CVS server with which communication is initiated.</p>
+
+<p>The following Java classes contain encryption items (the plug-in may contain both binary and source code):</p>
+
+<ul>
+ <li>org.eclipse.team.internal.ccvs.ssh.Blowfish</li>
+ <li>org.eclipse.team.internal.ccvs.ssh.Cipher</li>
+ <li>org.eclipse.team.internal.ccvs.ssh.Client</li>
+ <li>org.eclipse.team.internal.ccvs.ssh.ClientPacket</li>
+ <li>org.eclipse.team.internal.ccvs.ssh.Misc</li>
+</ul>
+
+<small>Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.</small>
</td></tr></table>
</body>
</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/build.properties b/bundles/org.eclipse.team.cvs.ssh/build.properties
new file mode 100644
index 000000000..71f31499b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/build.properties
@@ -0,0 +1,3 @@
+# Eclipse build contribution
+source.teamcvsssh.jar=src/
+bin.includes=about.html,plugin.xml,plugin.properties,*.jar \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/plugin.properties b/bundles/org.eclipse.team.cvs.ssh/plugin.properties
new file mode 100644
index 000000000..0150fce72
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/plugin.properties
@@ -0,0 +1 @@
+pluginName = CVS SSH Core \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/plugin.xml b/bundles/org.eclipse.team.cvs.ssh/plugin.xml
new file mode 100644
index 000000000..dbc05c22e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/plugin.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ name="%pluginName"
+ id="org.eclipse.team.cvs.ssh"
+ version="2.0.0"
+ class="org.eclipse.team.internal.ccvs.ssh.SSHPlugin"
+ provider-name="Object Technology International, Inc."
+>
+
+ <requires>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.team.cvs.core"/>
+ </requires>
+
+ <runtime>
+ <library name="teamcvsssh.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+ <extension id="ssh" point="org.eclipse.team.cvs.core.connectionmethods">
+ <adapter>
+ <run class="org.eclipse.team.internal.ccvs.ssh.SSHMethod">
+ <parameter name="trace" value="false" />
+ </run>
+ </adapter>
+ </extension>
+
+</plugin>
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Blowfish.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Blowfish.java
new file mode 100644
index 000000000..5cb92222e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Blowfish.java
@@ -0,0 +1,1214 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+public class Blowfish extends Cipher {
+ private int[] P = new int[18];
+ private int[][] S = new int[4][256];
+
+ private int lastEncipherXL = 0;
+ private int lastEncipherXR = 0;
+ private int lastDecipherXL = 0;
+ private int lastDecipherXR = 0;
+
+ private final static int[] INIT_DATA =
+ {
+ 0x243f6a88,
+ 0x85a308d3,
+ 0x13198a2e,
+ 0x03707344,
+ 0xa4093822,
+ 0x299f31d0,
+ 0x082efa98,
+ 0xec4e6c89,
+ 0x452821e6,
+ 0x38d01377,
+ 0xbe5466cf,
+ 0x34e90c6c,
+ 0xc0ac29b7,
+ 0xc97c50dd,
+ 0x3f84d5b5,
+ 0xb5470917,
+ 0x9216d5d9,
+ 0x8979fb1b,
+ 0xd1310ba6,
+ 0x98dfb5ac,
+ 0x2ffd72db,
+ 0xd01adfb7,
+ 0xb8e1afed,
+ 0x6a267e96,
+ 0xba7c9045,
+ 0xf12c7f99,
+ 0x24a19947,
+ 0xb3916cf7,
+ 0x0801f2e2,
+ 0x858efc16,
+ 0x636920d8,
+ 0x71574e69,
+ 0xa458fea3,
+ 0xf4933d7e,
+ 0x0d95748f,
+ 0x728eb658,
+ 0x718bcd58,
+ 0x82154aee,
+ 0x7b54a41d,
+ 0xc25a59b5,
+ 0x9c30d539,
+ 0x2af26013,
+ 0xc5d1b023,
+ 0x286085f0,
+ 0xca417918,
+ 0xb8db38ef,
+ 0x8e79dcb0,
+ 0x603a180e,
+ 0x6c9e0e8b,
+ 0xb01e8a3e,
+ 0xd71577c1,
+ 0xbd314b27,
+ 0x78af2fda,
+ 0x55605c60,
+ 0xe65525f3,
+ 0xaa55ab94,
+ 0x57489862,
+ 0x63e81440,
+ 0x55ca396a,
+ 0x2aab10b6,
+ 0xb4cc5c34,
+ 0x1141e8ce,
+ 0xa15486af,
+ 0x7c72e993,
+ 0xb3ee1411,
+ 0x636fbc2a,
+ 0x2ba9c55d,
+ 0x741831f6,
+ 0xce5c3e16,
+ 0x9b87931e,
+ 0xafd6ba33,
+ 0x6c24cf5c,
+ 0x7a325381,
+ 0x28958677,
+ 0x3b8f4898,
+ 0x6b4bb9af,
+ 0xc4bfe81b,
+ 0x66282193,
+ 0x61d809cc,
+ 0xfb21a991,
+ 0x487cac60,
+ 0x5dec8032,
+ 0xef845d5d,
+ 0xe98575b1,
+ 0xdc262302,
+ 0xeb651b88,
+ 0x23893e81,
+ 0xd396acc5,
+ 0x0f6d6ff3,
+ 0x83f44239,
+ 0x2e0b4482,
+ 0xa4842004,
+ 0x69c8f04a,
+ 0x9e1f9b5e,
+ 0x21c66842,
+ 0xf6e96c9a,
+ 0x670c9c61,
+ 0xabd388f0,
+ 0x6a51a0d2,
+ 0xd8542f68,
+ 0x960fa728,
+ 0xab5133a3,
+ 0x6eef0b6c,
+ 0x137a3be4,
+ 0xba3bf050,
+ 0x7efb2a98,
+ 0xa1f1651d,
+ 0x39af0176,
+ 0x66ca593e,
+ 0x82430e88,
+ 0x8cee8619,
+ 0x456f9fb4,
+ 0x7d84a5c3,
+ 0x3b8b5ebe,
+ 0xe06f75d8,
+ 0x85c12073,
+ 0x401a449f,
+ 0x56c16aa6,
+ 0x4ed3aa62,
+ 0x363f7706,
+ 0x1bfedf72,
+ 0x429b023d,
+ 0x37d0d724,
+ 0xd00a1248,
+ 0xdb0fead3,
+ 0x49f1c09b,
+ 0x075372c9,
+ 0x80991b7b,
+ 0x25d479d8,
+ 0xf6e8def7,
+ 0xe3fe501a,
+ 0xb6794c3b,
+ 0x976ce0bd,
+ 0x04c006ba,
+ 0xc1a94fb6,
+ 0x409f60c4,
+ 0x5e5c9ec2,
+ 0x196a2463,
+ 0x68fb6faf,
+ 0x3e6c53b5,
+ 0x1339b2eb,
+ 0x3b52ec6f,
+ 0x6dfc511f,
+ 0x9b30952c,
+ 0xcc814544,
+ 0xaf5ebd09,
+ 0xbee3d004,
+ 0xde334afd,
+ 0x660f2807,
+ 0x192e4bb3,
+ 0xc0cba857,
+ 0x45c8740f,
+ 0xd20b5f39,
+ 0xb9d3fbdb,
+ 0x5579c0bd,
+ 0x1a60320a,
+ 0xd6a100c6,
+ 0x402c7279,
+ 0x679f25fe,
+ 0xfb1fa3cc,
+ 0x8ea5e9f8,
+ 0xdb3222f8,
+ 0x3c7516df,
+ 0xfd616b15,
+ 0x2f501ec8,
+ 0xad0552ab,
+ 0x323db5fa,
+ 0xfd238760,
+ 0x53317b48,
+ 0x3e00df82,
+ 0x9e5c57bb,
+ 0xca6f8ca0,
+ 0x1a87562e,
+ 0xdf1769db,
+ 0xd542a8f6,
+ 0x287effc3,
+ 0xac6732c6,
+ 0x8c4f5573,
+ 0x695b27b0,
+ 0xbbca58c8,
+ 0xe1ffa35d,
+ 0xb8f011a0,
+ 0x10fa3d98,
+ 0xfd2183b8,
+ 0x4afcb56c,
+ 0x2dd1d35b,
+ 0x9a53e479,
+ 0xb6f84565,
+ 0xd28e49bc,
+ 0x4bfb9790,
+ 0xe1ddf2da,
+ 0xa4cb7e33,
+ 0x62fb1341,
+ 0xcee4c6e8,
+ 0xef20cada,
+ 0x36774c01,
+ 0xd07e9efe,
+ 0x2bf11fb4,
+ 0x95dbda4d,
+ 0xae909198,
+ 0xeaad8e71,
+ 0x6b93d5a0,
+ 0xd08ed1d0,
+ 0xafc725e0,
+ 0x8e3c5b2f,
+ 0x8e7594b7,
+ 0x8ff6e2fb,
+ 0xf2122b64,
+ 0x8888b812,
+ 0x900df01c,
+ 0x4fad5ea0,
+ 0x688fc31c,
+ 0xd1cff191,
+ 0xb3a8c1ad,
+ 0x2f2f2218,
+ 0xbe0e1777,
+ 0xea752dfe,
+ 0x8b021fa1,
+ 0xe5a0cc0f,
+ 0xb56f74e8,
+ 0x18acf3d6,
+ 0xce89e299,
+ 0xb4a84fe0,
+ 0xfd13e0b7,
+ 0x7cc43b81,
+ 0xd2ada8d9,
+ 0x165fa266,
+ 0x80957705,
+ 0x93cc7314,
+ 0x211a1477,
+ 0xe6ad2065,
+ 0x77b5fa86,
+ 0xc75442f5,
+ 0xfb9d35cf,
+ 0xebcdaf0c,
+ 0x7b3e89a0,
+ 0xd6411bd3,
+ 0xae1e7e49,
+ 0x00250e2d,
+ 0x2071b35e,
+ 0x226800bb,
+ 0x57b8e0af,
+ 0x2464369b,
+ 0xf009b91e,
+ 0x5563911d,
+ 0x59dfa6aa,
+ 0x78c14389,
+ 0xd95a537f,
+ 0x207d5ba2,
+ 0x02e5b9c5,
+ 0x83260376,
+ 0x6295cfa9,
+ 0x11c81968,
+ 0x4e734a41,
+ 0xb3472dca,
+ 0x7b14a94a,
+ 0x1b510052,
+ 0x9a532915,
+ 0xd60f573f,
+ 0xbc9bc6e4,
+ 0x2b60a476,
+ 0x81e67400,
+ 0x08ba6fb5,
+ 0x571be91f,
+ 0xf296ec6b,
+ 0x2a0dd915,
+ 0xb6636521,
+ 0xe7b9f9b6,
+ 0xff34052e,
+ 0xc5855664,
+ 0x53b02d5d,
+ 0xa99f8fa1,
+ 0x08ba4799,
+ 0x6e85076a,
+ 0x4b7a70e9,
+ 0xb5b32944,
+ 0xdb75092e,
+ 0xc4192623,
+ 0xad6ea6b0,
+ 0x49a7df7d,
+ 0x9cee60b8,
+ 0x8fedb266,
+ 0xecaa8c71,
+ 0x699a17ff,
+ 0x5664526c,
+ 0xc2b19ee1,
+ 0x193602a5,
+ 0x75094c29,
+ 0xa0591340,
+ 0xe4183a3e,
+ 0x3f54989a,
+ 0x5b429d65,
+ 0x6b8fe4d6,
+ 0x99f73fd6,
+ 0xa1d29c07,
+ 0xefe830f5,
+ 0x4d2d38e6,
+ 0xf0255dc1,
+ 0x4cdd2086,
+ 0x8470eb26,
+ 0x6382e9c6,
+ 0x021ecc5e,
+ 0x09686b3f,
+ 0x3ebaefc9,
+ 0x3c971814,
+ 0x6b6a70a1,
+ 0x687f3584,
+ 0x52a0e286,
+ 0xb79c5305,
+ 0xaa500737,
+ 0x3e07841c,
+ 0x7fdeae5c,
+ 0x8e7d44ec,
+ 0x5716f2b8,
+ 0xb03ada37,
+ 0xf0500c0d,
+ 0xf01c1f04,
+ 0x0200b3ff,
+ 0xae0cf51a,
+ 0x3cb574b2,
+ 0x25837a58,
+ 0xdc0921bd,
+ 0xd19113f9,
+ 0x7ca92ff6,
+ 0x94324773,
+ 0x22f54701,
+ 0x3ae5e581,
+ 0x37c2dadc,
+ 0xc8b57634,
+ 0x9af3dda7,
+ 0xa9446146,
+ 0x0fd0030e,
+ 0xecc8c73e,
+ 0xa4751e41,
+ 0xe238cd99,
+ 0x3bea0e2f,
+ 0x3280bba1,
+ 0x183eb331,
+ 0x4e548b38,
+ 0x4f6db908,
+ 0x6f420d03,
+ 0xf60a04bf,
+ 0x2cb81290,
+ 0x24977c79,
+ 0x5679b072,
+ 0xbcaf89af,
+ 0xde9a771f,
+ 0xd9930810,
+ 0xb38bae12,
+ 0xdccf3f2e,
+ 0x5512721f,
+ 0x2e6b7124,
+ 0x501adde6,
+ 0x9f84cd87,
+ 0x7a584718,
+ 0x7408da17,
+ 0xbc9f9abc,
+ 0xe94b7d8c,
+ 0xec7aec3a,
+ 0xdb851dfa,
+ 0x63094366,
+ 0xc464c3d2,
+ 0xef1c1847,
+ 0x3215d908,
+ 0xdd433b37,
+ 0x24c2ba16,
+ 0x12a14d43,
+ 0x2a65c451,
+ 0x50940002,
+ 0x133ae4dd,
+ 0x71dff89e,
+ 0x10314e55,
+ 0x81ac77d6,
+ 0x5f11199b,
+ 0x043556f1,
+ 0xd7a3c76b,
+ 0x3c11183b,
+ 0x5924a509,
+ 0xf28fe6ed,
+ 0x97f1fbfa,
+ 0x9ebabf2c,
+ 0x1e153c6e,
+ 0x86e34570,
+ 0xeae96fb1,
+ 0x860e5e0a,
+ 0x5a3e2ab3,
+ 0x771fe71c,
+ 0x4e3d06fa,
+ 0x2965dcb9,
+ 0x99e71d0f,
+ 0x803e89d6,
+ 0x5266c825,
+ 0x2e4cc978,
+ 0x9c10b36a,
+ 0xc6150eba,
+ 0x94e2ea78,
+ 0xa5fc3c53,
+ 0x1e0a2df4,
+ 0xf2f74ea7,
+ 0x361d2b3d,
+ 0x1939260f,
+ 0x19c27960,
+ 0x5223a708,
+ 0xf71312b6,
+ 0xebadfe6e,
+ 0xeac31f66,
+ 0xe3bc4595,
+ 0xa67bc883,
+ 0xb17f37d1,
+ 0x018cff28,
+ 0xc332ddef,
+ 0xbe6c5aa5,
+ 0x65582185,
+ 0x68ab9802,
+ 0xeecea50f,
+ 0xdb2f953b,
+ 0x2aef7dad,
+ 0x5b6e2f84,
+ 0x1521b628,
+ 0x29076170,
+ 0xecdd4775,
+ 0x619f1510,
+ 0x13cca830,
+ 0xeb61bd96,
+ 0x0334fe1e,
+ 0xaa0363cf,
+ 0xb5735c90,
+ 0x4c70a239,
+ 0xd59e9e0b,
+ 0xcbaade14,
+ 0xeecc86bc,
+ 0x60622ca7,
+ 0x9cab5cab,
+ 0xb2f3846e,
+ 0x648b1eaf,
+ 0x19bdf0ca,
+ 0xa02369b9,
+ 0x655abb50,
+ 0x40685a32,
+ 0x3c2ab4b3,
+ 0x319ee9d5,
+ 0xc021b8f7,
+ 0x9b540b19,
+ 0x875fa099,
+ 0x95f7997e,
+ 0x623d7da8,
+ 0xf837889a,
+ 0x97e32d77,
+ 0x11ed935f,
+ 0x16681281,
+ 0x0e358829,
+ 0xc7e61fd6,
+ 0x96dedfa1,
+ 0x7858ba99,
+ 0x57f584a5,
+ 0x1b227263,
+ 0x9b83c3ff,
+ 0x1ac24696,
+ 0xcdb30aeb,
+ 0x532e3054,
+ 0x8fd948e4,
+ 0x6dbc3128,
+ 0x58ebf2ef,
+ 0x34c6ffea,
+ 0xfe28ed61,
+ 0xee7c3c73,
+ 0x5d4a14d9,
+ 0xe864b7e3,
+ 0x42105d14,
+ 0x203e13e0,
+ 0x45eee2b6,
+ 0xa3aaabea,
+ 0xdb6c4f15,
+ 0xfacb4fd0,
+ 0xc742f442,
+ 0xef6abbb5,
+ 0x654f3b1d,
+ 0x41cd2105,
+ 0xd81e799e,
+ 0x86854dc7,
+ 0xe44b476a,
+ 0x3d816250,
+ 0xcf62a1f2,
+ 0x5b8d2646,
+ 0xfc8883a0,
+ 0xc1c7b6a3,
+ 0x7f1524c3,
+ 0x69cb7492,
+ 0x47848a0b,
+ 0x5692b285,
+ 0x095bbf00,
+ 0xad19489d,
+ 0x1462b174,
+ 0x23820e00,
+ 0x58428d2a,
+ 0x0c55f5ea,
+ 0x1dadf43e,
+ 0x233f7061,
+ 0x3372f092,
+ 0x8d937e41,
+ 0xd65fecf1,
+ 0x6c223bdb,
+ 0x7cde3759,
+ 0xcbee7460,
+ 0x4085f2a7,
+ 0xce77326e,
+ 0xa6078084,
+ 0x19f8509e,
+ 0xe8efd855,
+ 0x61d99735,
+ 0xa969a7aa,
+ 0xc50c06c2,
+ 0x5a04abfc,
+ 0x800bcadc,
+ 0x9e447a2e,
+ 0xc3453484,
+ 0xfdd56705,
+ 0x0e1e9ec9,
+ 0xdb73dbd3,
+ 0x105588cd,
+ 0x675fda79,
+ 0xe3674340,
+ 0xc5c43465,
+ 0x713e38d8,
+ 0x3d28f89e,
+ 0xf16dff20,
+ 0x153e21e7,
+ 0x8fb03d4a,
+ 0xe6e39f2b,
+ 0xdb83adf7,
+ 0xe93d5a68,
+ 0x948140f7,
+ 0xf64c261c,
+ 0x94692934,
+ 0x411520f7,
+ 0x7602d4f7,
+ 0xbcf46b2e,
+ 0xd4a20068,
+ 0xd4082471,
+ 0x3320f46a,
+ 0x43b7d4b7,
+ 0x500061af,
+ 0x1e39f62e,
+ 0x97244546,
+ 0x14214f74,
+ 0xbf8b8840,
+ 0x4d95fc1d,
+ 0x96b591af,
+ 0x70f4ddd3,
+ 0x66a02f45,
+ 0xbfbc09ec,
+ 0x03bd9785,
+ 0x7fac6dd0,
+ 0x31cb8504,
+ 0x96eb27b3,
+ 0x55fd3941,
+ 0xda2547e6,
+ 0xabca0a9a,
+ 0x28507825,
+ 0x530429f4,
+ 0x0a2c86da,
+ 0xe9b66dfb,
+ 0x68dc1462,
+ 0xd7486900,
+ 0x680ec0a4,
+ 0x27a18dee,
+ 0x4f3ffea2,
+ 0xe887ad8c,
+ 0xb58ce006,
+ 0x7af4d6b6,
+ 0xaace1e7c,
+ 0xd3375fec,
+ 0xce78a399,
+ 0x406b2a42,
+ 0x20fe9e35,
+ 0xd9f385b9,
+ 0xee39d7ab,
+ 0x3b124e8b,
+ 0x1dc9faf7,
+ 0x4b6d1856,
+ 0x26a36631,
+ 0xeae397b2,
+ 0x3a6efa74,
+ 0xdd5b4332,
+ 0x6841e7f7,
+ 0xca7820fb,
+ 0xfb0af54e,
+ 0xd8feb397,
+ 0x454056ac,
+ 0xba489527,
+ 0x55533a3a,
+ 0x20838d87,
+ 0xfe6ba9b7,
+ 0xd096954b,
+ 0x55a867bc,
+ 0xa1159a58,
+ 0xcca92963,
+ 0x99e1db33,
+ 0xa62a4a56,
+ 0x3f3125f9,
+ 0x5ef47e1c,
+ 0x9029317c,
+ 0xfdf8e802,
+ 0x04272f70,
+ 0x80bb155c,
+ 0x05282ce3,
+ 0x95c11548,
+ 0xe4c66d22,
+ 0x48c1133f,
+ 0xc70f86dc,
+ 0x07f9c9ee,
+ 0x41041f0f,
+ 0x404779a4,
+ 0x5d886e17,
+ 0x325f51eb,
+ 0xd59bc0d1,
+ 0xf2bcc18f,
+ 0x41113564,
+ 0x257b7834,
+ 0x602a9c60,
+ 0xdff8e8a3,
+ 0x1f636c1b,
+ 0x0e12b4c2,
+ 0x02e1329e,
+ 0xaf664fd1,
+ 0xcad18115,
+ 0x6b2395e0,
+ 0x333e92e1,
+ 0x3b240b62,
+ 0xeebeb922,
+ 0x85b2a20e,
+ 0xe6ba0d99,
+ 0xde720c8c,
+ 0x2da2f728,
+ 0xd0127845,
+ 0x95b794fd,
+ 0x647d0862,
+ 0xe7ccf5f0,
+ 0x5449a36f,
+ 0x877d48fa,
+ 0xc39dfd27,
+ 0xf33e8d1e,
+ 0x0a476341,
+ 0x992eff74,
+ 0x3a6f6eab,
+ 0xf4f8fd37,
+ 0xa812dc60,
+ 0xa1ebddf8,
+ 0x991be14c,
+ 0xdb6e6b0d,
+ 0xc67b5510,
+ 0x6d672c37,
+ 0x2765d43b,
+ 0xdcd0e804,
+ 0xf1290dc7,
+ 0xcc00ffa3,
+ 0xb5390f92,
+ 0x690fed0b,
+ 0x667b9ffb,
+ 0xcedb7d9c,
+ 0xa091cf0b,
+ 0xd9155ea3,
+ 0xbb132f88,
+ 0x515bad24,
+ 0x7b9479bf,
+ 0x763bd6eb,
+ 0x37392eb3,
+ 0xcc115979,
+ 0x8026e297,
+ 0xf42e312d,
+ 0x6842ada7,
+ 0xc66a2b3b,
+ 0x12754ccc,
+ 0x782ef11c,
+ 0x6a124237,
+ 0xb79251e7,
+ 0x06a1bbe6,
+ 0x4bfb6350,
+ 0x1a6b1018,
+ 0x11caedfa,
+ 0x3d25bdd8,
+ 0xe2e1c3c9,
+ 0x44421659,
+ 0x0a121386,
+ 0xd90cec6e,
+ 0xd5abea2a,
+ 0x64af674e,
+ 0xda86a85f,
+ 0xbebfe988,
+ 0x64e4c3fe,
+ 0x9dbc8057,
+ 0xf0f7c086,
+ 0x60787bf8,
+ 0x6003604d,
+ 0xd1fd8346,
+ 0xf6381fb0,
+ 0x7745ae04,
+ 0xd736fccc,
+ 0x83426b33,
+ 0xf01eab71,
+ 0xb0804187,
+ 0x3c005e5f,
+ 0x77a057be,
+ 0xbde8ae24,
+ 0x55464299,
+ 0xbf582e61,
+ 0x4e58f48f,
+ 0xf2ddfda2,
+ 0xf474ef38,
+ 0x8789bdc2,
+ 0x5366f9c3,
+ 0xc8b38e74,
+ 0xb475f255,
+ 0x46fcd9b9,
+ 0x7aeb2661,
+ 0x8b1ddf84,
+ 0x846a0e79,
+ 0x915f95e2,
+ 0x466e598e,
+ 0x20b45770,
+ 0x8cd55591,
+ 0xc902de4c,
+ 0xb90bace1,
+ 0xbb8205d0,
+ 0x11a86248,
+ 0x7574a99e,
+ 0xb77f19b6,
+ 0xe0a9dc09,
+ 0x662d09a1,
+ 0xc4324633,
+ 0xe85a1f02,
+ 0x09f0be8c,
+ 0x4a99a025,
+ 0x1d6efe10,
+ 0x1ab93d1d,
+ 0x0ba5a4df,
+ 0xa186f20f,
+ 0x2868f169,
+ 0xdcb7da83,
+ 0x573906fe,
+ 0xa1e2ce9b,
+ 0x4fcd7f52,
+ 0x50115e01,
+ 0xa70683fa,
+ 0xa002b5c4,
+ 0x0de6d027,
+ 0x9af88c27,
+ 0x773f8641,
+ 0xc3604c06,
+ 0x61a806b5,
+ 0xf0177a28,
+ 0xc0f586e0,
+ 0x006058aa,
+ 0x30dc7d62,
+ 0x11e69ed7,
+ 0x2338ea63,
+ 0x53c2dd94,
+ 0xc2c21634,
+ 0xbbcbee56,
+ 0x90bcb6de,
+ 0xebfc7da1,
+ 0xce591d76,
+ 0x6f05e409,
+ 0x4b7c0188,
+ 0x39720a3d,
+ 0x7c927c24,
+ 0x86e3725f,
+ 0x724d9db9,
+ 0x1ac15bb4,
+ 0xd39eb8fc,
+ 0xed545578,
+ 0x08fca5b5,
+ 0xd83d7cd3,
+ 0x4dad0fc4,
+ 0x1e50ef5e,
+ 0xb161e6f8,
+ 0xa28514d9,
+ 0x6c51133c,
+ 0x6fd5c7e7,
+ 0x56e14ec4,
+ 0x362abfce,
+ 0xddc6c837,
+ 0xd79a3234,
+ 0x92638212,
+ 0x670efa8e,
+ 0x406000e0,
+ 0x3a39ce37,
+ 0xd3faf5cf,
+ 0xabc27737,
+ 0x5ac52d1b,
+ 0x5cb0679e,
+ 0x4fa33742,
+ 0xd3822740,
+ 0x99bc9bbe,
+ 0xd5118e9d,
+ 0xbf0f7315,
+ 0xd62d1c7e,
+ 0xc700c47b,
+ 0xb78c1b6b,
+ 0x21a19045,
+ 0xb26eb1be,
+ 0x6a366eb4,
+ 0x5748ab2f,
+ 0xbc946e79,
+ 0xc6a376d2,
+ 0x6549c2c8,
+ 0x530ff8ee,
+ 0x468dde7d,
+ 0xd5730a1d,
+ 0x4cd04dc6,
+ 0x2939bbdb,
+ 0xa9ba4650,
+ 0xac9526e8,
+ 0xbe5ee304,
+ 0xa1fad5f0,
+ 0x6a2d519a,
+ 0x63ef8ce2,
+ 0x9a86ee22,
+ 0xc089c2b8,
+ 0x43242ef6,
+ 0xa51e03aa,
+ 0x9cf2d0a4,
+ 0x83c061ba,
+ 0x9be96a4d,
+ 0x8fe51550,
+ 0xba645bd6,
+ 0x2826a2f9,
+ 0xa73a3ae1,
+ 0x4ba99586,
+ 0xef5562e9,
+ 0xc72fefd3,
+ 0xf752f7da,
+ 0x3f046f69,
+ 0x77fa0a59,
+ 0x80e4a915,
+ 0x87b08601,
+ 0x9b09e6ad,
+ 0x3b3ee593,
+ 0xe990fd5a,
+ 0x9e34d797,
+ 0x2cf0b7d9,
+ 0x022b8b51,
+ 0x96d5ac3a,
+ 0x017da67d,
+ 0xd1cf3ed6,
+ 0x7c7d2d28,
+ 0x1f9f25cf,
+ 0xadf2b89b,
+ 0x5ad6b472,
+ 0x5a88f54c,
+ 0xe029ac71,
+ 0xe019a5e6,
+ 0x47b0acfd,
+ 0xed93fa9b,
+ 0xe8d3c48d,
+ 0x283b57cc,
+ 0xf8d56629,
+ 0x79132e28,
+ 0x785f0191,
+ 0xed756055,
+ 0xf7960e44,
+ 0xe3d35e8c,
+ 0x15056dd4,
+ 0x88f46dba,
+ 0x03a16125,
+ 0x0564f0bd,
+ 0xc3eb9e15,
+ 0x3c9057a2,
+ 0x97271aec,
+ 0xa93a072a,
+ 0x1b3f6d9b,
+ 0x1e6321f5,
+ 0xf59c66fb,
+ 0x26dcf319,
+ 0x7533d928,
+ 0xb155fdf5,
+ 0x03563482,
+ 0x8aba3cbb,
+ 0x28517711,
+ 0xc20ad9f8,
+ 0xabcc5167,
+ 0xccad925f,
+ 0x4de81751,
+ 0x3830dc8e,
+ 0x379d5862,
+ 0x9320f991,
+ 0xea7a90c2,
+ 0xfb3e7bce,
+ 0x5121ce64,
+ 0x774fbe32,
+ 0xa8b6e37e,
+ 0xc3293d46,
+ 0x48de5369,
+ 0x6413e680,
+ 0xa2ae0810,
+ 0xdd6db224,
+ 0x69852dfd,
+ 0x09072166,
+ 0xb39a460a,
+ 0x6445c0dd,
+ 0x586cdecf,
+ 0x1c20c8ae,
+ 0x5bbef7dd,
+ 0x1b588d40,
+ 0xccd2017f,
+ 0x6bb4e3bb,
+ 0xdda26a7e,
+ 0x3a59ff45,
+ 0x3e350a44,
+ 0xbcb4cdd5,
+ 0x72eacea8,
+ 0xfa6484bb,
+ 0x8d6612ae,
+ 0xbf3c6f47,
+ 0xd29be463,
+ 0x542f5d9e,
+ 0xaec2771b,
+ 0xf64e6370,
+ 0x740e0d8d,
+ 0xe75b1357,
+ 0xf8721671,
+ 0xaf537d5d,
+ 0x4040cb08,
+ 0x4eb4e2cc,
+ 0x34d2466a,
+ 0x0115af84,
+ 0xe1b00428,
+ 0x95983a1d,
+ 0x06b89fb4,
+ 0xce6ea048,
+ 0x6f3f3b82,
+ 0x3520ab82,
+ 0x011a1d4b,
+ 0x277227f8,
+ 0x611560b1,
+ 0xe7933fdc,
+ 0xbb3a792b,
+ 0x344525bd,
+ 0xa08839e1,
+ 0x51ce794b,
+ 0x2f32c9b7,
+ 0xa01fbac9,
+ 0xe01cc87e,
+ 0xbcc7d1f6,
+ 0xcf0111c3,
+ 0xa1e8aac7,
+ 0x1a908749,
+ 0xd44fbd9a,
+ 0xd0dadecb,
+ 0xd50ada38,
+ 0x0339c32a,
+ 0xc6913667,
+ 0x8df9317c,
+ 0xe0b12b4f,
+ 0xf79e59b7,
+ 0x43f5bb3a,
+ 0xf2d519ff,
+ 0x27d9459c,
+ 0xbf97222c,
+ 0x15e6fc2a,
+ 0x0f91fc71,
+ 0x9b941525,
+ 0xfae59361,
+ 0xceb69ceb,
+ 0xc2a86459,
+ 0x12baa8d1,
+ 0xb6c1075e,
+ 0xe3056a0c,
+ 0x10d25065,
+ 0xcb03a442,
+ 0xe0ec6e0e,
+ 0x1698db3b,
+ 0x4c98a0be,
+ 0x3278e964,
+ 0x9f1f9532,
+ 0xe0d392df,
+ 0xd3a0342b,
+ 0x8971f21e,
+ 0x1b0a7441,
+ 0x4ba3348c,
+ 0xc5be7120,
+ 0xc37632d8,
+ 0xdf359f8d,
+ 0x9b992f2e,
+ 0xe60b6f47,
+ 0x0fe3f11d,
+ 0xe54cda54,
+ 0x1edad891,
+ 0xce6279cf,
+ 0xcd3e7e6f,
+ 0x1618b166,
+ 0xfd2c1d05,
+ 0x848fd2c5,
+ 0xf6fb2299,
+ 0xf523f357,
+ 0xa6327623,
+ 0x93a83531,
+ 0x56cccd02,
+ 0xacf08162,
+ 0x5a75ebb5,
+ 0x6e163697,
+ 0x88d273cc,
+ 0xde966292,
+ 0x81b949d0,
+ 0x4c50901b,
+ 0x71c65614,
+ 0xe6c6c7bd,
+ 0x327a140a,
+ 0x45e1d006,
+ 0xc3f27b9a,
+ 0xc9aa53fd,
+ 0x62a80f00,
+ 0xbb25bfe2,
+ 0x35bdd2f6,
+ 0x71126905,
+ 0xb2040222,
+ 0xb6cbcf7c,
+ 0xcd769c2b,
+ 0x53113ec0,
+ 0x1640e3d3,
+ 0x38abbd60,
+ 0x2547adf0,
+ 0xba38209c,
+ 0xf746ce76,
+ 0x77afa1c5,
+ 0x20756060,
+ 0x85cbfe4e,
+ 0x8ae88dd8,
+ 0x7aaaf9b0,
+ 0x4cf9aa7e,
+ 0x1948c25c,
+ 0x02fb8a8c,
+ 0x01c36ae4,
+ 0xd6ebe1f9,
+ 0x90d4f869,
+ 0xa65cdea0,
+ 0x3f09252d,
+ 0xc208e69f,
+ 0xb74e6132,
+ 0xce77e25b,
+ 0x578fdfe3,
+ 0x3ac372e6 };
+public void decipher(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ int xL = lastDecipherXL;
+ int xR = lastDecipherXR;
+
+ int dataXL;
+ int dataXR;
+
+ int[] xLxR = new int[2];
+
+ for (int i = 0; i < len; i = i + 8) {
+ dataXL = (src[srcOff + i] & 0xff) | ((src[srcOff + i + 1] & 0xff) << 8) | ((src[srcOff + i + 2] & 0xff) << 16) | ((src[srcOff + i + 3] & 0xff) << 24);
+ dataXR = (src[srcOff + i + 4] & 0xff) | ((src[srcOff + i + 5] & 0xff) << 8) | ((src[srcOff + i + 6] & 0xff) << 16) | ((src[srcOff + i + 7] & 0xff) << 24);
+
+ decipher(dataXL, dataXR, xLxR);
+
+ xL ^= xLxR[0];
+ xR ^= xLxR[1];
+
+ dst[dstOff + i] = (byte) (xL & 0xff);
+ dst[dstOff + i + 1] = (byte) ((xL >> 8) & 0xff);
+ dst[dstOff + i + 2] = (byte) ((xL >> 16) & 0xff);
+ dst[dstOff + i + 3] = (byte) ((xL >> 24) & 0xff);
+ dst[dstOff + i + 4] = (byte) (xR & 0xff);
+ dst[dstOff + i + 5] = (byte) ((xR >> 8) & 0xff);
+ dst[dstOff + i + 6] = (byte) ((xR >> 16) & 0xff);
+ dst[dstOff + i + 7] = (byte) ((xR >> 24) & 0xff);
+
+ xL = dataXL;
+ xR = dataXR;
+ }
+
+ lastDecipherXL = xL;
+ lastDecipherXR = xR;
+}
+private void decipher(int xL, int xR, int[] xLxR) {
+ xL ^= P[17];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[16];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[15];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[14];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[13];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[12];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[11];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[10];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[9];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[8];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[7];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[6];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[5];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[4];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[3];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[2];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[1];
+ xR ^= P[0];
+
+ xLxR[0] = xR;
+ xLxR[1] = xL;
+}
+public void encipher(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+ int xL = lastEncipherXL;
+ int xR = lastEncipherXR;
+
+ int[] xLxR = new int[2];
+
+ for (int i = 0; i < len; i = i + 8) {
+ xL ^= (src[srcOff + i] & 0xff) | ((src[srcOff + i + 1] & 0xff) << 8) | ((src[srcOff + i + 2] & 0xff) << 16) | ((src[srcOff + i + 3] & 0xff) << 24);
+ xR ^= (src[srcOff + i + 4] & 0xff) | ((src[srcOff + i + 5] & 0xff) << 8) | ((src[srcOff + i + 6] & 0xff) << 16) | ((src[srcOff + i + 7] & 0xff) << 24);
+
+ encipher(xL, xR, xLxR);
+
+ xL = xLxR[0];
+ xR = xLxR[1];
+
+ dst[dstOff + i] = (byte) (xL & 0xff);
+ dst[dstOff + i + 1] = (byte) ((xL >> 8) & 0xff);
+ dst[dstOff + i + 2] = (byte) ((xL >> 16) & 0xff);
+ dst[dstOff + i + 3] = (byte) ((xL >> 24) & 0xff);
+ dst[dstOff + i + 4] = (byte) (xR & 0xff);
+ dst[dstOff + i + 5] = (byte) ((xR >> 8) & 0xff);
+ dst[dstOff + i + 6] = (byte) ((xR >> 16) & 0xff);
+ dst[dstOff + i + 7] = (byte) ((xR >> 24) & 0xff);
+ }
+
+ lastEncipherXL = xL;
+ lastEncipherXR = xR;
+}
+private void encipher(int xL, int xR, int[] xLxR) {
+ xL ^= P[0];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[1];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[2];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[3];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[4];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[5];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[6];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[7];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[8];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[9];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[10];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[11];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[12];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[13];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[14];
+ xR ^= (((S[0][(xL >> 24) & 0xff] + S[1][(xL >> 16) & 0xff]) ^ (S[2][(xL >> 8) & 0xff])) + (S[3][xL & 0xff])) ^ P[15];
+ xL ^= (((S[0][(xR >> 24) & 0xff] + S[1][(xR >> 16) & 0xff]) ^ (S[2][(xR >> 8) & 0xff])) + (S[3][xR & 0xff])) ^ P[16];
+ xR ^= P[17];
+
+ xLxR[0] = xR;
+ xLxR[1] = xL;
+}
+public void setKey(byte key[]) {
+ int data = 0;
+ int offset = 0;
+
+ for (int i = 0; i < P.length; ++i) {
+ P[i] = INIT_DATA[offset++];
+ }
+
+ for (int i = 0; i < S.length; ++i) {
+ for (int j = 0; j < S[i].length; ++j) {
+ S[i][j] = INIT_DATA[offset++];
+ }
+ }
+
+ offset = 0;
+ for (int i = 0; i < P.length; ++i) {
+ data = 0;
+ for (int k = 0; k < 4; ++k) {
+ data = (data << 8) | (key[offset++] & 0xff);
+ if (offset >= key.length) {
+ offset = 0;
+ }
+ }
+ P[i] = P[i] ^ data;
+ }
+
+ int[] lr = new int[] { 0, 0 };
+ for (int i = 0; i < P.length; i += 2) {
+ encipher(lr[0], lr[1], lr);
+ P[i] = lr[0];
+ P[i + 1] = lr[1];
+ }
+
+ for (int i = 0; i < S.length; ++i) {
+ for (int j = 0; j < S[i].length; j += 2) {
+ encipher(lr[0], lr[1], lr);
+ S[i][j] = lr[0];
+ S[i][j + 1] = lr[1];
+ }
+ }
+
+ lastEncipherXL = 0;
+ lastEncipherXR = 0;
+ lastDecipherXL = 0;
+ lastDecipherXR = 0;
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Cipher.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Cipher.java
new file mode 100644
index 000000000..83a397955
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Cipher.java
@@ -0,0 +1,19 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+public abstract class Cipher {
+public abstract void decipher(byte[] src, int srcPos, byte[] dst, int dstPos, int len);
+public abstract void encipher(byte[] src, int srcPos, byte[] dst, int dstPos, int len);
+public static Cipher getInstance(String algorithm) {
+ try {
+ Class c = Class.forName("org.eclipse.team.internal.ccvs.ssh." + algorithm);
+ return (Cipher) c.newInstance();
+ } catch (Exception e) {
+ return null;
+ }
+}
+public abstract void setKey(byte[] key);
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Client.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Client.java
new file mode 100644
index 000000000..1e94daebc
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Client.java
@@ -0,0 +1,644 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * An SSH 1.5 client..
+ */
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import org.eclipse.team.internal.ccvs.core.connection.CVSAuthenticationException;
+
+public class Client {
+ // client identification string
+ private static final String clientId = "SSH-1.5-Java 1.2.2\n";
+
+ // server identification string
+ private static String serverId = null;
+
+ // maximum outgoing packet size
+ private static final int MAX_CLIENT_PACKET_SIZE = 1024;
+
+ // packet types
+ private static final int SSH_MSG_DISCONNECT = 1;
+ private static final int SSH_SMSG_PUBLIC_KEY = 2;
+ private static final int SSH_CMSG_SESSION_KEY = 3;
+ private static final int SSH_CMSG_USER = 4;
+ private static final int SSH_CMSG_AUTH_PASSWORD = 9;
+ private static final int SSH_CMSG_REQUEST_PTY = 10;
+ private static final int SSH_CMSG_EXEC_SHELL = 12;
+ private static final int SSH_CMSG_EXEC_CMD = 13;
+ private static final int SSH_SMSG_SUCCESS = 14;
+ private static final int SSH_SMSG_FAILURE = 15;
+ private static final int SSH_CMSG_STDIN_DATA = 16;
+ private static final int SSH_SMSG_STDOUT_DATA = 17;
+ private static final int SSH_SMSG_STDERR_DATA = 18;
+ private static final int SSH_SMSG_EXITSTATUS = 20;
+ private static final int SSH_CMSG_EXIT_CONFIRMATION = 33;
+ private static final int SSH_MSG_DEBUG = 36;
+
+ // cipher names
+ private static String[] cipherNames = { "None", "IDEA", "DES", "3DES", "TSS", "RC4", "Blowfish" };
+
+ // cipher types
+ private static int SSH_CIPHER_NONE = 0;
+ private static int SSH_CIPHER_IDEA = 1;
+ private static int SSH_CIPHER_DES = 2;
+ private static int SSH_CIPHER_3DES = 3;
+ private static int SSH_CIPHER_TSS = 4;
+ private static int SSH_CIPHER_RC4 = 5;
+ private static int SSH_CIPHER_BLOWFISH = 6;
+
+ // preferred cipher types
+ private int[] preferredCipherTypes = { SSH_CIPHER_BLOWFISH };
+
+ // authentication methods
+ private final int SSH_AUTH_RHOSTS = 1;
+ private final int SSH_AUTH_RSA = 2;
+ private final int SSH_AUTH_PASSWORD = 3;
+ private final int SSH_AUTH_RHOSTS_RSA = 4;
+
+ private String host;
+ private int port;
+ private String username;
+ private String password;
+ private String command;
+
+ private Socket socket;
+ private InputStream socketIn;
+ private OutputStream socketOut;
+ private InputStream is;
+ private OutputStream os;
+ private boolean connected = false;
+ private int timeout = -1;
+
+ private Cipher cipher = null;
+
+ private class StandardInputStream extends InputStream {
+ private ServerPacket packet = null;
+ private InputStream buffer = null;
+ private int buflen = 0;
+ private boolean atEnd = false;
+ private boolean closed = false;
+ private int exitStatus = 0;
+
+ public int available() throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ int available = buffer == null ? 0 : buffer.available();
+
+ if (available == 0 && socketIn.available() > 0) {
+ fill();
+ available = buffer.available();
+ }
+
+ return available;
+ }
+
+ public void close() throws IOException {
+ if (!closed) {
+ closed = true;
+ if (packet != null) {
+ packet.close(false);
+ buffer = null;
+ packet = null;
+ buflen = 0;
+ }
+ }
+ }
+
+ public int read() throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ if (atEnd) {
+ return -1;
+ }
+
+ if (buffer == null || buffer.available() == 0) {
+ fill();
+ }
+
+ return buffer.read();
+ }
+
+ public int read(byte b[], int off, int len) throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ if (atEnd) {
+ return -1;
+ }
+
+ if (buffer == null || buffer.available() == 0) {
+ fill();
+ }
+
+ return buffer.read(b, off, len);
+ }
+
+ private void fill() throws IOException {
+ if (buffer != null) {
+ buffer.close();
+ }
+
+ packet = new ServerPacket(socketIn, cipher);
+ int packetType = packet.getType();
+
+ switch (packetType) {
+ case SSH_SMSG_STDOUT_DATA :
+ case SSH_SMSG_STDERR_DATA :
+ case SSH_MSG_DEBUG :
+ buffer = packet.getInputStream();
+ buflen = Misc.readInt(buffer);
+ break;
+ case SSH_SMSG_EXITSTATUS :
+ buffer = null;
+ buflen = 0;
+ atEnd = true;
+ InputStream pis = packet.getInputStream();
+ exitStatus = Misc.readInt(pis);
+ pis.close();
+ send(SSH_CMSG_EXIT_CONFIRMATION, null);
+ break;
+ default :
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+ }
+ }
+
+ private class StandardOutputStream extends OutputStream {
+ private int MAX_BUFFER_SIZE = MAX_CLIENT_PACKET_SIZE;
+ private byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ private int bufpos = 0;
+ private boolean closed = false;
+
+ public void close() throws IOException {
+ if (!closed) {
+ try {
+ flush();
+ } finally {
+ closed = true;
+ }
+ }
+ }
+
+ public void flush() throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ if (bufpos > 0) {
+ send(SSH_CMSG_STDIN_DATA, buffer, 0, bufpos);
+ bufpos = 0;
+ }
+ }
+
+ public void write(int b) throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ buffer[bufpos++] = (byte) b;
+
+ if (bufpos == MAX_BUFFER_SIZE) {
+ flush();
+ }
+ }
+
+ public void write(byte b[], int off, int len) throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ int bytesWritten = 0;
+ int totalBytesWritten = 0;
+
+ if (bufpos > 0) {
+ bytesWritten = Math.min(MAX_BUFFER_SIZE - bufpos, len);
+ System.arraycopy(b, off, buffer, bufpos, bytesWritten);
+ bufpos += bytesWritten;
+ totalBytesWritten += bytesWritten;
+
+ if (bufpos == MAX_BUFFER_SIZE) {
+ flush();
+ }
+ }
+
+ while (len - totalBytesWritten >= MAX_BUFFER_SIZE) {
+ send(SSH_CMSG_STDIN_DATA, b, off + totalBytesWritten, MAX_BUFFER_SIZE);
+ totalBytesWritten += MAX_BUFFER_SIZE;
+ }
+
+ if (totalBytesWritten < len) {
+ bytesWritten = len - totalBytesWritten;
+ System.arraycopy(b, off + totalBytesWritten, buffer, 0, bytesWritten);
+ bufpos += bytesWritten;
+ }
+ }
+ }
+public Client(InputStream socketIn, OutputStream socketOut, String username, String password) {
+ this.socketIn = socketIn;
+ this.socketOut = socketOut;
+ this.username = username;
+ this.password = password;
+}
+public Client(InputStream socketIn, OutputStream socketOut, String username, String password, String command) {
+ this(socketIn, socketOut, username, password);
+ this.command = command;
+}
+public Client(String host, int port, String username, String password) {
+ this.host = host;
+ this.port = port;
+ this.username = username;
+ this.password = password;
+}
+public Client(String host, int port, String username, String password, String command) {
+ this(host, port, username, password);
+ this.command = command;
+}
+public Client(String host, int port, String username, String password, String command, int timeout) {
+ this(host, port, username, password, command);
+ this.timeout = timeout;
+}
+/**
+ * Close all streams and sockets.
+ */
+private void cleanup() throws IOException {
+ try {
+ if (is != null)
+ is.close();
+ } finally {
+ try {
+ if (os != null)
+ os.close();
+ } finally {
+ try {
+ if (socketIn != null)
+ socketIn.close();
+ } finally {
+ try {
+ if (socketOut != null)
+ socketOut.close();
+ } finally {
+ try {
+ if (socket != null)
+ socket.close();
+ } finally {
+ socket = null;
+ }
+ }
+ }
+ }
+ }
+}
+/**
+ * Connect to the remote server. If an exception is thrown, the caller
+ * can asssume that all streams and sockets are closed.
+ */
+public void connect() throws IOException, CVSAuthenticationException {
+ // If we're already connected, just ignore the invokation
+ if (connected)
+ return;
+
+ // Otherwise, set up the connection
+ try {
+
+ // Create the socket (the socket should always be null here)
+ if (socket == null) {
+ try {
+ socket = new Socket(host, port);
+ } catch (InterruptedIOException e) {
+ // If we get this exception, chances are the host is not responding
+ throw new InterruptedIOException(SSHPlugin.getResourceString("Client.socket", new Object[] {host}));
+
+ }
+ if (timeout >= 0) {
+ socket.setSoTimeout(timeout * 1000);
+ }
+ socketIn = new BufferedInputStream(socket.getInputStream());
+ socketOut = new BufferedOutputStream(socket.getOutputStream());
+ }
+
+ // read the ssh server id. The socket creation may of failed if the
+ // server cannot accept our connection request. We don't expect the
+ // socket to be closed at this point.
+ StringBuffer buf = new StringBuffer();
+ int c;
+ while ((c = socketIn.read()) != '\n') {
+ if (c == -1)
+ throw new IOException(SSHPlugin.getResourceString("Client.socketClosed"));
+ buf.append((char) c);
+ }
+ serverId = buf.toString();
+
+ // send our id.
+ socketOut.write(clientId.getBytes());
+ socketOut.flush();
+
+ login();
+
+ // start a shell and enter interactive session or start by
+ // executing the given command.
+ if( command == null ) {
+ startShell();
+ } else {
+ executeCommand();
+ }
+
+ is = new StandardInputStream();
+ os = new StandardOutputStream();
+
+ connected = true;
+ } catch (IOException e) {
+ // If an exception occurs while connected, make sure we disconnect before passing the exception on
+ try {
+ cleanup();
+ } finally {
+ throw e;
+ }
+ }
+}
+/**
+ * Terminate the connection to the server.
+ */
+public void disconnect() throws IOException {
+ if (connected) {
+ connected = false;
+ send(SSH_MSG_DISCONNECT, null);
+ cleanup();
+ }
+}
+public InputStream getInputStream() throws IOException {
+ if (!connected) {
+ throw new IOException(SSHPlugin.getResourceString("Client.notConnected"));
+ }
+
+ return is;
+}
+public OutputStream getOutputStream() throws IOException {
+ if (!connected) {
+ throw new IOException(SSHPlugin.getResourceString("Client.notConnected"));
+ }
+
+ return os;
+}
+
+private void startShell() throws IOException {
+ ServerPacket packet = null;
+ int packetType;
+
+ send_SSH_CMSG_REQUEST_PTY();
+
+ try {
+ packet = new ServerPacket(socketIn, cipher);
+ packetType = packet.getType();
+
+ if (packetType != SSH_SMSG_SUCCESS) {
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+ } finally {
+ if (packet != null) {
+ packet.close(true /*perform crc check*/);
+ }
+ }
+
+ send(SSH_CMSG_EXEC_SHELL, null);
+}
+
+private void executeCommand() throws IOException {
+ send(SSH_CMSG_EXEC_CMD, command);
+}
+
+private void login() throws IOException, CVSAuthenticationException {
+ ServerPacket packet = null;
+ int packetType;
+
+ try {
+ packet = new ServerPacket(socketIn, cipher);
+ packetType = packet.getType();
+
+ if (packetType != SSH_SMSG_PUBLIC_KEY) {
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+
+ receive_SSH_SMSG_PUBLIC_KEY(packet);
+ } finally {
+ if (packet != null) {
+ packet.close(true);
+ }
+ }
+
+ try {
+ packet = new ServerPacket(socketIn, cipher);
+ packetType = packet.getType();
+
+ if (packetType != SSH_SMSG_SUCCESS) {
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+ } finally {
+ if (packet != null) {
+ packet.close(true);
+ }
+ }
+
+ send(SSH_CMSG_USER, username);
+
+ try {
+ packet = new ServerPacket(socketIn, cipher);
+ packetType = packet.getType();
+
+ if (packetType != SSH_SMSG_FAILURE) {
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+ } finally {
+ if (packet != null) {
+ packet.close(true);
+ }
+ }
+
+ send(SSH_CMSG_AUTH_PASSWORD, password);
+
+ try {
+ packet = new ServerPacket(socketIn, cipher);
+ packetType = packet.getType();
+
+ if (packetType == SSH_SMSG_FAILURE) {
+ throw new CVSAuthenticationException(SSHPlugin.getResourceString("Client.authenticationFailed"));
+ }
+
+ if (packetType != SSH_SMSG_SUCCESS) {
+ throw new IOException(SSHPlugin.getResourceString("Client.packetType", new Object[] {new Integer(packetType)} ));
+ }
+ } finally {
+ if (packet != null) {
+ packet.close(true);
+ }
+ }
+}
+private void receive_SSH_SMSG_PUBLIC_KEY(ServerPacket packet) throws IOException {
+ InputStream pis = packet.getInputStream();
+
+ byte[] anti_spoofing_cookie = new byte[8];
+ Misc.readFully(pis, anti_spoofing_cookie);
+
+ byte[] server_key_bits = new byte[4];
+ Misc.readFully(pis, server_key_bits);
+
+ byte[] server_key_public_exponent = Misc.readMpInt(pis);
+ byte[] server_key_public_modulus = Misc.readMpInt(pis);
+
+ byte[] host_key_bits = new byte[4];
+ Misc.readFully(pis, host_key_bits);
+
+ byte[] host_key_public_exponent = Misc.readMpInt(pis);
+ byte[] host_key_public_modulus = Misc.readMpInt(pis);
+
+ byte[] protocol_flags = new byte[4];
+ Misc.readFully(pis, protocol_flags);
+
+ byte[] supported_ciphers_mask = new byte[4];
+ Misc.readFully(pis, supported_ciphers_mask);
+
+ byte[] supported_authentications_mask = new byte[4];
+ Misc.readFully(pis, supported_authentications_mask);
+
+ pis.close();
+
+ send_SSH_CMSG_SESSION_KEY(anti_spoofing_cookie, server_key_public_modulus, host_key_public_modulus, supported_ciphers_mask, server_key_public_exponent, host_key_public_exponent);
+}
+private void send(int packetType, String s) throws IOException {
+ byte[] data = s == null ? new byte[0] : s.getBytes("UTF-8");
+ send(packetType, data, 0, data.length);
+}
+private void send(int packetType, byte[] data, int off, int len) throws IOException {
+ data = data == null ? null : Misc.lengthEncode(data, off, len);
+ ClientPacket packet = new ClientPacket(packetType, data, cipher);
+ socketOut.write(packet.getBytes());
+ socketOut.flush();
+}
+private void send_SSH_CMSG_REQUEST_PTY() throws IOException {
+ byte packet_type = SSH_CMSG_REQUEST_PTY;
+
+ byte[] termType = Misc.lengthEncode("dumb".getBytes(), 0, 4);
+ byte[] row = {0, 0, 0, 0};
+ byte[] col = {0, 0, 0, 0};
+ byte[] XPixels = {0, 0, 0, 0};
+ byte[] YPixels = {0, 0, 0, 0};
+ byte[] terminalModes = {0};
+
+ byte[] data = new byte[termType.length + row.length + col.length + XPixels.length + YPixels.length + terminalModes.length];
+
+ int offset = 0;
+ System.arraycopy(termType, 0, data, offset, termType.length);
+
+ offset += termType.length;
+ System.arraycopy(row, 0, data, offset, row.length);
+
+ offset += row.length;
+ System.arraycopy(col, 0, data, offset, col.length);
+
+ offset += col.length;
+ System.arraycopy(XPixels, 0, data, offset, XPixels.length);
+
+ offset += XPixels.length;
+ System.arraycopy(YPixels, 0, data, offset, YPixels.length);
+
+ offset += YPixels.length;
+ System.arraycopy(terminalModes, 0, data, offset, terminalModes.length);
+
+ ClientPacket packet = new ClientPacket(packet_type, data, cipher);
+ socketOut.write(packet.getBytes());
+ socketOut.flush();
+}
+private void send_SSH_CMSG_SESSION_KEY(byte[] anti_spoofing_cookie, byte[] server_key_public_modulus, byte[] host_key_public_modulus, byte[] supported_ciphers_mask, byte[] server_key_public_exponent, byte[] host_key_public_exponent) throws IOException {
+ byte packet_type = SSH_CMSG_SESSION_KEY;
+
+ // session_id
+ byte[] session_id = new byte[host_key_public_modulus.length + server_key_public_modulus.length + anti_spoofing_cookie.length];
+
+ int offset = 0;
+ System.arraycopy(host_key_public_modulus, 0, session_id, offset, host_key_public_modulus.length);
+
+ offset += host_key_public_modulus.length;
+ System.arraycopy(server_key_public_modulus, 0, session_id, offset, server_key_public_modulus.length);
+
+ offset += server_key_public_modulus.length;
+ System.arraycopy(anti_spoofing_cookie, 0, session_id, offset, anti_spoofing_cookie.length);
+
+ session_id = Misc.md5(session_id);
+
+ // cipher_type
+ byte cipher_type = 0;
+ boolean foundSupportedCipher = false;
+
+ for (int i = 0; i < preferredCipherTypes.length && !foundSupportedCipher; ++i) {
+ cipher_type = (byte) preferredCipherTypes[i];
+ foundSupportedCipher = (supported_ciphers_mask[3] & (byte) (1 << cipher_type)) != 0;
+ }
+
+ if (!foundSupportedCipher) {
+ throw new IOException(SSHPlugin.getResourceString("Client.cipher"));
+ }
+
+ // session_key
+ byte[] session_key = new byte[32];
+ byte[] session_key_xored = new byte[32];
+ byte[] session_key_encrypted = null;
+
+ Misc.random(session_key, 0, session_key.length, true);
+ System.arraycopy(session_key, 0, session_key_xored, 0, session_key.length);
+ Misc.xor(session_key_xored, 0, session_id, 0, session_key_xored, 0, session_id.length);
+
+ byte[] result = Misc.encryptRSAPkcs1(session_key_xored, server_key_public_exponent, server_key_public_modulus);
+ result = Misc.encryptRSAPkcs1(result, host_key_public_exponent, host_key_public_modulus);
+
+ session_key_encrypted = new byte[result.length + 2];
+ session_key_encrypted[1] = (byte) ((8 * result.length) & 0xff);
+ session_key_encrypted[0] = (byte) (((8 * result.length) >> 8) & 0xff);
+
+ for (int i = 0; i < result.length; i++) {
+ session_key_encrypted[i + 2] = result[i];
+ }
+
+ // protocol_flags
+ byte[] protocol_flags = {0, 0, 0, 0};
+
+ // data
+ byte[] data = new byte[1 + anti_spoofing_cookie.length + session_key_encrypted.length + protocol_flags.length];
+
+ offset = 0;
+ data[offset++] = (byte) cipher_type;
+
+ System.arraycopy(anti_spoofing_cookie, 0, data, offset, anti_spoofing_cookie.length);
+
+ offset += anti_spoofing_cookie.length;
+ System.arraycopy(session_key_encrypted, 0, data, offset, session_key_encrypted.length);
+
+ offset += session_key_encrypted.length;
+ System.arraycopy(protocol_flags, 0, data, offset, protocol_flags.length);
+
+ // cipher
+ cipher = Cipher.getInstance(cipherNames[cipher_type]);
+ cipher.setKey(session_key);
+
+ // packet
+ ClientPacket packet = new ClientPacket(packet_type, data, null);
+ socketOut.write(packet.getBytes());
+ socketOut.flush();
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ClientPacket.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ClientPacket.java
new file mode 100644
index 000000000..36d7a658c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ClientPacket.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+class ClientPacket extends Packet {
+ byte[] packet;
+public ClientPacket(int type, byte[] data, Cipher cipher) {
+ packetLength = data == null ? 5 : data.length + 5;
+ paddingLength = 8 - (packetLength % 8);
+ packetType = type;
+ packet = new byte[4 + paddingLength + packetLength];
+
+ int packetOff = 0;
+ Misc.writeInt(packetLength, packet, packetOff);
+ packetOff += 4;
+
+ if (cipher == null) {
+ for (int i = 0; i < paddingLength; i++) {
+ packet[packetOff++] = 0;
+ }
+ } else {
+ Misc.random(packet, packetOff, paddingLength, false);
+ packetOff += paddingLength;
+ }
+
+ packet[packetOff++] = (byte) packetType;
+
+ if (data != null) {
+ for (int i = 0; i < data.length; ++i) {
+ packet[packetOff++] = data[i];
+ }
+ }
+
+ long crc = Misc.crc32(packet, 4, packet.length - 8, 0);
+ Misc.writeInt((int) crc, packet, packetOff);
+ packetOff += 4;
+
+ if (cipher != null) {
+ cipher.encipher(packet, 4, packet, 4, packet.length - 4);
+ }
+}
+public byte[] getBytes() {
+ return packet;
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Misc.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Misc.java
new file mode 100644
index 000000000..e8561a5ff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Misc.java
@@ -0,0 +1,449 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+class Misc {
+ private static MessageDigest md5 = null;
+ private static SecureRandom random = null;
+ private static long crc32_tab[] = {
+ 0x00000000L,
+ 0x77073096L,
+ 0xee0e612cL,
+ 0x990951baL,
+ 0x076dc419L,
+ 0x706af48fL,
+ 0xe963a535L,
+ 0x9e6495a3L,
+ 0x0edb8832L,
+ 0x79dcb8a4L,
+ 0xe0d5e91eL,
+ 0x97d2d988L,
+ 0x09b64c2bL,
+ 0x7eb17cbdL,
+ 0xe7b82d07L,
+ 0x90bf1d91L,
+ 0x1db71064L,
+ 0x6ab020f2L,
+ 0xf3b97148L,
+ 0x84be41deL,
+ 0x1adad47dL,
+ 0x6ddde4ebL,
+ 0xf4d4b551L,
+ 0x83d385c7L,
+ 0x136c9856L,
+ 0x646ba8c0L,
+ 0xfd62f97aL,
+ 0x8a65c9ecL,
+ 0x14015c4fL,
+ 0x63066cd9L,
+ 0xfa0f3d63L,
+ 0x8d080df5L,
+ 0x3b6e20c8L,
+ 0x4c69105eL,
+ 0xd56041e4L,
+ 0xa2677172L,
+ 0x3c03e4d1L,
+ 0x4b04d447L,
+ 0xd20d85fdL,
+ 0xa50ab56bL,
+ 0x35b5a8faL,
+ 0x42b2986cL,
+ 0xdbbbc9d6L,
+ 0xacbcf940L,
+ 0x32d86ce3L,
+ 0x45df5c75L,
+ 0xdcd60dcfL,
+ 0xabd13d59L,
+ 0x26d930acL,
+ 0x51de003aL,
+ 0xc8d75180L,
+ 0xbfd06116L,
+ 0x21b4f4b5L,
+ 0x56b3c423L,
+ 0xcfba9599L,
+ 0xb8bda50fL,
+ 0x2802b89eL,
+ 0x5f058808L,
+ 0xc60cd9b2L,
+ 0xb10be924L,
+ 0x2f6f7c87L,
+ 0x58684c11L,
+ 0xc1611dabL,
+ 0xb6662d3dL,
+ 0x76dc4190L,
+ 0x01db7106L,
+ 0x98d220bcL,
+ 0xefd5102aL,
+ 0x71b18589L,
+ 0x06b6b51fL,
+ 0x9fbfe4a5L,
+ 0xe8b8d433L,
+ 0x7807c9a2L,
+ 0x0f00f934L,
+ 0x9609a88eL,
+ 0xe10e9818L,
+ 0x7f6a0dbbL,
+ 0x086d3d2dL,
+ 0x91646c97L,
+ 0xe6635c01L,
+ 0x6b6b51f4L,
+ 0x1c6c6162L,
+ 0x856530d8L,
+ 0xf262004eL,
+ 0x6c0695edL,
+ 0x1b01a57bL,
+ 0x8208f4c1L,
+ 0xf50fc457L,
+ 0x65b0d9c6L,
+ 0x12b7e950L,
+ 0x8bbeb8eaL,
+ 0xfcb9887cL,
+ 0x62dd1ddfL,
+ 0x15da2d49L,
+ 0x8cd37cf3L,
+ 0xfbd44c65L,
+ 0x4db26158L,
+ 0x3ab551ceL,
+ 0xa3bc0074L,
+ 0xd4bb30e2L,
+ 0x4adfa541L,
+ 0x3dd895d7L,
+ 0xa4d1c46dL,
+ 0xd3d6f4fbL,
+ 0x4369e96aL,
+ 0x346ed9fcL,
+ 0xad678846L,
+ 0xda60b8d0L,
+ 0x44042d73L,
+ 0x33031de5L,
+ 0xaa0a4c5fL,
+ 0xdd0d7cc9L,
+ 0x5005713cL,
+ 0x270241aaL,
+ 0xbe0b1010L,
+ 0xc90c2086L,
+ 0x5768b525L,
+ 0x206f85b3L,
+ 0xb966d409L,
+ 0xce61e49fL,
+ 0x5edef90eL,
+ 0x29d9c998L,
+ 0xb0d09822L,
+ 0xc7d7a8b4L,
+ 0x59b33d17L,
+ 0x2eb40d81L,
+ 0xb7bd5c3bL,
+ 0xc0ba6cadL,
+ 0xedb88320L,
+ 0x9abfb3b6L,
+ 0x03b6e20cL,
+ 0x74b1d29aL,
+ 0xead54739L,
+ 0x9dd277afL,
+ 0x04db2615L,
+ 0x73dc1683L,
+ 0xe3630b12L,
+ 0x94643b84L,
+ 0x0d6d6a3eL,
+ 0x7a6a5aa8L,
+ 0xe40ecf0bL,
+ 0x9309ff9dL,
+ 0x0a00ae27L,
+ 0x7d079eb1L,
+ 0xf00f9344L,
+ 0x8708a3d2L,
+ 0x1e01f268L,
+ 0x6906c2feL,
+ 0xf762575dL,
+ 0x806567cbL,
+ 0x196c3671L,
+ 0x6e6b06e7L,
+ 0xfed41b76L,
+ 0x89d32be0L,
+ 0x10da7a5aL,
+ 0x67dd4accL,
+ 0xf9b9df6fL,
+ 0x8ebeeff9L,
+ 0x17b7be43L,
+ 0x60b08ed5L,
+ 0xd6d6a3e8L,
+ 0xa1d1937eL,
+ 0x38d8c2c4L,
+ 0x4fdff252L,
+ 0xd1bb67f1L,
+ 0xa6bc5767L,
+ 0x3fb506ddL,
+ 0x48b2364bL,
+ 0xd80d2bdaL,
+ 0xaf0a1b4cL,
+ 0x36034af6L,
+ 0x41047a60L,
+ 0xdf60efc3L,
+ 0xa867df55L,
+ 0x316e8eefL,
+ 0x4669be79L,
+ 0xcb61b38cL,
+ 0xbc66831aL,
+ 0x256fd2a0L,
+ 0x5268e236L,
+ 0xcc0c7795L,
+ 0xbb0b4703L,
+ 0x220216b9L,
+ 0x5505262fL,
+ 0xc5ba3bbeL,
+ 0xb2bd0b28L,
+ 0x2bb45a92L,
+ 0x5cb36a04L,
+ 0xc2d7ffa7L,
+ 0xb5d0cf31L,
+ 0x2cd99e8bL,
+ 0x5bdeae1dL,
+ 0x9b64c2b0L,
+ 0xec63f226L,
+ 0x756aa39cL,
+ 0x026d930aL,
+ 0x9c0906a9L,
+ 0xeb0e363fL,
+ 0x72076785L,
+ 0x05005713L,
+ 0x95bf4a82L,
+ 0xe2b87a14L,
+ 0x7bb12baeL,
+ 0x0cb61b38L,
+ 0x92d28e9bL,
+ 0xe5d5be0dL,
+ 0x7cdcefb7L,
+ 0x0bdbdf21L,
+ 0x86d3d2d4L,
+ 0xf1d4e242L,
+ 0x68ddb3f8L,
+ 0x1fda836eL,
+ 0x81be16cdL,
+ 0xf6b9265bL,
+ 0x6fb077e1L,
+ 0x18b74777L,
+ 0x88085ae6L,
+ 0xff0f6a70L,
+ 0x66063bcaL,
+ 0x11010b5cL,
+ 0x8f659effL,
+ 0xf862ae69L,
+ 0x616bffd3L,
+ 0x166ccf45L,
+ 0xa00ae278L,
+ 0xd70dd2eeL,
+ 0x4e048354L,
+ 0x3903b3c2L,
+ 0xa7672661L,
+ 0xd06016f7L,
+ 0x4969474dL,
+ 0x3e6e77dbL,
+ 0xaed16a4aL,
+ 0xd9d65adcL,
+ 0x40df0b66L,
+ 0x37d83bf0L,
+ 0xa9bcae53L,
+ 0xdebb9ec5L,
+ 0x47b2cf7fL,
+ 0x30b5ffe9L,
+ 0xbdbdf21cL,
+ 0xcabac28aL,
+ 0x53b39330L,
+ 0x24b4a3a6L,
+ 0xbad03605L,
+ 0xcdd70693L,
+ 0x54de5729L,
+ 0x23d967bfL,
+ 0xb3667a2eL,
+ 0xc4614ab8L,
+ 0x5d681b02L,
+ 0x2a6f2b94L,
+ 0xb40bbe37L,
+ 0xc30c8ea1L,
+ 0x5a05df1bL,
+ 0x2d02ef8dL
+ };
+static public long crc32(byte[] b, int off, int len) {
+ return crc32(b, off, len, 0);
+}
+/**
+ * Compute the crc Cyclic Redundancy Check, with the polynomial 0xedb88320,
+ * The polynomial is X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ * We take it "backwards" and put the highest-order term in the lowest-order bit.
+ * The X^32 term is "implied"; the LSB is the X^31 term, etc.
+ * The X^0 term (usually shown as "+1") results in the MSB being 1.
+ * so the poly is 0x04c11db7 (used for Ethernet)
+ * The buf will be the Padding, Packet type, and Data fields.
+ * The crc is computed before any encryption.
+ * R =X^n * M rem P M message P polynomial crc R : crc calculated.
+ * T(x) = x^n * M(x) + R(x) property: T rem P = 0
+ */
+static public long crc32(byte[] b, int off, int len, long crc32val) {
+ for (int i = 0; i < len; i++) {
+ crc32val = crc32_tab[(int) ((crc32val ^ b[off + i]) & 0xff)] ^ (crc32val >> 8);
+ }
+
+ return crc32val;
+}
+static public byte[] lengthEncode(byte[] b, int off, int len) throws IOException {
+ byte[] result = new byte[len + 4];
+ writeInt(len, result, 0);
+ System.arraycopy(b, off, result, 4, len);
+ return result;
+}
+static public byte[] readMpInt(InputStream is) throws IOException {
+ int a = (byte) is.read();
+ int b = (byte) is.read();
+
+ if(a == -1 || b == -1){
+ throw new IOException(SSHPlugin.getResourceString("stream"));
+ }
+
+ int bits = (a << 8) + b;
+ int bytes = (bits + 7) / 8;
+
+ byte[] result = new byte[bytes];
+
+ readFully(is, result);
+
+ return result;
+}
+public static byte[] md5(byte[] b) {
+ if (md5 == null) {
+ try {
+ md5 = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ }
+ }
+
+ return md5.digest(b);
+}
+public static byte[] md5(String s) {
+ return md5(s.getBytes());
+}
+public static void readFully(InputStream is, byte[] b) throws IOException {
+ readFully(is, b, 0, b.length);
+}
+public static void readFully(InputStream is, byte[] b, int off, int len) throws IOException {
+ int bytesRead = 0;
+ int totalBytesRead = 0;
+
+ while (totalBytesRead < len) {
+ bytesRead = is.read(b, totalBytesRead + off, len - totalBytesRead);
+
+ if (bytesRead == -1) {
+ throw new IOException(SSHPlugin.getResourceString("stream"));
+ }
+
+ totalBytesRead += bytesRead;
+ }
+}
+public static int readInt(byte[] arr, int off) throws IOException {
+ int a = arr[off] & 0xff;
+ int b = arr[off + 1] & 0xff;
+ int c = arr[off + 2] & 0xff;
+ int d = arr[off + 3] & 0xff;
+
+ return (a << 24) + (b << 16) + (c << 8) + d;
+}
+public static int readInt(InputStream is) throws IOException {
+ int a = is.read();
+ int b = is.read();
+ int c = is.read();
+ int d = is.read();
+
+ if (a == -1 || b == -1 || c == -1 || d == -1) {
+ throw new IOException(SSHPlugin.getResourceString("stream"));
+ }
+
+ return (a << 24) + (b << 16) + (c << 8) + d;
+}
+public static void skipFully(InputStream is, long n) throws IOException {
+ while (n != 0) {
+ int b = is.read();
+
+ if (b == -1) {
+ if (n > 0) {
+ throw new IOException(SSHPlugin.getResourceString("stream"));
+ }
+
+ return;
+ }
+
+ --n;
+ }
+}
+public static void writeInt(int i, byte[] b, int off) {
+ b[off] = (byte) ((i >> 24) & 0xff);
+ b[off + 1] = (byte) ((i >> 16) & 0xff);
+ b[off + 2] = (byte) ((i >> 8) & 0xff);
+ b[off + 3] = (byte) (i & 0xff);
+}
+static public void xor(byte[] src_a, int off_a, byte[] src_b, int off_b, byte[] dst, int dst_off, int len) {
+ for(int i = 0; i < len; ++i){
+ dst[i + dst_off] = (byte) (src_a[i + off_a] ^ src_b[i + off_b]);
+ }
+}
+static public void random(byte[] b, int off, int len, boolean allowZeroBytes) {
+ if (random == null) {
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ }
+ }
+
+ for(int i = off; i < off + len; ++i){
+ do {
+ b[i] = (byte) random.nextInt();
+ } while(!allowZeroBytes && b[i] == 0);
+ }
+}
+static public byte[] encryptRSAPkcs1(byte[] data, byte[] public_key_exponent, byte[] public_key_modulus) {
+ byte[] block;
+
+ int offset = 0;
+ block = new byte[public_key_modulus.length];
+ block[offset++] = 0;
+ block[offset++] = 2;
+
+ Misc.random(block, offset, block.length - data.length - 3, false);
+ offset += block.length - data.length - 3;
+
+ block[offset++] = 0;
+
+ for (int i = 0; i < data.length; i++){
+ block[offset++] = data[i];
+ }
+
+ BigInteger m, e, message;
+ byte[] messageByte;
+
+ m = new BigInteger(1, public_key_modulus);
+ e = new BigInteger(1, public_key_exponent);
+ message = new BigInteger(1, block);
+ message = message.modPow(e, m);
+
+ byte[] messageByteTemp = message.toByteArray();
+ messageByte = new byte[public_key_modulus.length];
+
+ int tempOffset = 0;
+ while (messageByteTemp[tempOffset] == 0){
+ tempOffset++;
+ }
+
+ for (int i = messageByte.length - messageByteTemp.length + tempOffset; i < messageByte.length; i++){
+ messageByte[i] = messageByteTemp[tempOffset++];
+ }
+
+ return messageByte;
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Packet.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Packet.java
new file mode 100644
index 000000000..87cdc0648
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/Packet.java
@@ -0,0 +1,14 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+class Packet {
+ protected int packetLength;
+ protected int paddingLength;
+ protected int packetType;
+public int getType() {
+ return packetType;
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHMethod.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHMethod.java
new file mode 100644
index 000000000..64908f8eb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHMethod.java
@@ -0,0 +1,26 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IConnectionMethod;
+import org.eclipse.team.ccvs.core.IServerConnection;
+
+public class SSHMethod implements IConnectionMethod {
+ /**
+ * @see IConnectionMethod#getName
+ */
+ public String getName() {
+ return "extssh";
+ }
+
+ /**
+ * @see IConnectionMethod#createConnection
+ */
+ public IServerConnection createConnection(ICVSRepositoryLocation repositoryRoot, String password) {
+ return new SSHServerConnection(repositoryRoot, password);
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPlugin.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPlugin.java
new file mode 100644
index 000000000..7cf1eec15
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPlugin.java
@@ -0,0 +1 @@
+package org.eclipse.team.internal.ccvs.ssh; /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ import org.eclipse.core.runtime.IPluginDescriptor; import org.eclipse.core.runtime.Plugin; import java.util.ResourceBundle; import java.util.MissingResourceException; import java.text.MessageFormat; public class SSHPlugin extends Plugin { private static ResourceBundle resourceBundle; public static String ID = "org.eclipse.team.cvs.ssh"; /** * Constructor for SSHPlugin */ public SSHPlugin(IPluginDescriptor d) { super(d); try { resourceBundle = ResourceBundle.getBundle("org.eclipse.team.internal.ccvs.ssh.SSHPluginResources"); } catch (MissingResourceException e) { resourceBundle = null; } } /** * Gets a string from the resource bundle. * We don't want to crash because of a missing String. * Returns the key if not found. */ public static String getResourceString(String key) { try { return resourceBundle.getString(key); } catch (MissingResourceException e) { return key; } catch (NullPointerException e) { return "!" + key + "!"; } } /** * Gets a string from the resource bundle and binds it * with the given arguments. If the key is not found, * return the key. */ public static String getResourceString(String key, Object[] args) { try { return MessageFormat.format(getResourceString(key), args); } catch (MissingResourceException e) { return key; } catch (NullPointerException e) { return "!" + key + "!"; } } } \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPluginResources.properties b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPluginResources.properties
new file mode 100644
index 000000000..926cc871e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHPluginResources.properties
@@ -0,0 +1,20 @@
+###########################
+# (c) Copyright IBM Corp. 2000, 2001.
+# All Rights Reserved.
+###########################
+
+# String resources for vcm.core.cvs.ssh
+###########################
+
+closed=Closed
+stream=Unexpected end of stream
+
+Client.packetType=unexpected packet type: {0}
+Client.notConnected=Not connected
+Client.cipher=Supported cipher not found
+Client.socketClosed=Connection closed by server
+Client.authenticationFailed=Invalid username or password
+Client.socket=Cannot connect to host: {0}
+
+ServerPacket.crc=crc error
+
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHServerConnection.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHServerConnection.java
new file mode 100644
index 000000000..511064fff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/SSHServerConnection.java
@@ -0,0 +1,77 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.team.internal.ccvs.core.connection.CVSAuthenticationException;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IServerConnection;
+
+public class SSHServerConnection implements IServerConnection {
+
+ // command to start remote cvs in server mode
+ private static final String INVOKE_SVR_CMD = "cvs server";
+
+ private static final int DEFAULT_PORT = 22;
+
+ // cvs format for the repository (e.g. :extssh:user@host:/home/cvs/repo)
+ private ICVSRepositoryLocation location;
+
+ // password for user specified in repository location string
+ private String password;
+
+ // incoming from remote host
+ InputStream inputStream;
+
+ // outgoing to remote host
+ OutputStream outputStream;
+
+ // ssh client
+ Client client;
+
+ protected SSHServerConnection(ICVSRepositoryLocation location, String password) {
+ this.location = location;
+ this.password = password;
+ }
+
+ public void close() throws IOException {
+ client.disconnect();
+ }
+ /**
+ * Returns the <code>InputStream</code> used to read data from the
+ * server.
+ */
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+ /**
+ * Returns the <code>OutputStream</code> used to send data to the
+ * server.
+ */
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Opens the connection and invokes cvs in server mode.
+ *
+ * @see Connection.open()
+ */
+ public void open() throws IOException, CVSAuthenticationException {
+ String hostname = location.getHost();
+ String username = location.getUsername();
+ int port = location.getPort();
+ if (port == location.USE_DEFAULT_PORT)
+ port = DEFAULT_PORT;
+ // create the connection using host, username, and password
+ client = new Client(hostname, port, username, password, INVOKE_SVR_CMD, location.getTimeout());
+ client.connect();
+ inputStream = client.getInputStream();
+ outputStream = client.getOutputStream();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ServerPacket.java b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ServerPacket.java
new file mode 100644
index 000000000..c3e7b1022
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ssh/src/org/eclipse/team/internal/ccvs/ssh/ServerPacket.java
@@ -0,0 +1,158 @@
+package org.eclipse.team.internal.ccvs.ssh;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+class ServerPacket extends Packet {
+ private PacketInputStream pis = null;
+
+ private static class PacketInputStream extends FilterInputStream {
+ private static int MAX_BUFFER_SIZE = 1024;
+
+ private byte[] buffer = new byte[MAX_BUFFER_SIZE];
+ private int bufpos = 0;
+ private int buflen = 0;
+ private int bufrem = 0;
+
+ private long remaining = 0;
+ private Cipher cipher = null;
+
+ private long crc = 0;
+ private boolean closed = false;
+
+ public PacketInputStream(InputStream in, long length, Cipher cipher) {
+ super(in);
+
+ this.remaining = length;
+ this.cipher = cipher;
+ }
+
+ public int available() throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ return (int) Math.min(remaining - 4, Integer.MAX_VALUE);
+ }
+
+ public void close() throws IOException {
+ close(true);
+ }
+
+ public void close(boolean doCrcCheck) throws IOException {
+ if (!closed) {
+ try {
+ long toRead = doCrcCheck ? remaining - 4 : remaining;
+
+ try {
+ Misc.skipFully(this, toRead);
+ } catch(IOException e) {
+ // we tried our best, keep going
+ }
+
+ if(doCrcCheck) {
+ if ((int) crc != Misc.readInt(buffer, bufpos)) {
+ throw new IOException(SSHPlugin.getResourceString("ServerPacket.crc"));
+ }
+ }
+ } finally {
+ closed = true;
+ }
+ }
+ }
+
+ private void fill() throws IOException {
+ if (bufrem > 0) {
+ System.arraycopy(buffer, bufpos, buffer, 0, bufrem);
+ }
+
+ int totalBytesRead = bufrem;
+ int read = 0;
+ int toRead = (int)Math.min(remaining - totalBytesRead, MAX_BUFFER_SIZE - totalBytesRead);
+
+ while (toRead > 0) {
+ read = in.read(buffer, totalBytesRead, toRead);
+
+ if (read == -1) {
+ throw new IOException(SSHPlugin.getResourceString("stream"));
+ }
+
+ totalBytesRead += read;
+ toRead -= read;
+ }
+
+ bufpos = 0;
+
+ buflen = (totalBytesRead / 8) * 8;
+ bufrem = totalBytesRead - buflen;
+
+ if (cipher != null) {
+ cipher.decipher(buffer, 0, buffer, 0, buflen);
+ }
+
+ crc = Misc.crc32(buffer, 0, buflen == remaining ? buflen - 4 : buflen, crc);
+ }
+
+ public int read() throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ if (remaining - 4 == 0) {
+ return -1;
+ }
+
+ if (bufpos == buflen) {
+ fill();
+ }
+
+ int b = buffer[bufpos] & 0xff;
+
+ ++bufpos;
+ --remaining;
+
+ return b;
+ }
+
+ public int read(byte b[], int off, int len) throws IOException {
+ if (closed) {
+ throw new IOException(SSHPlugin.getResourceString("closed"));
+ }
+
+ if (remaining - 4 == 0) {
+ return -1;
+ }
+
+ if (bufpos == buflen) {
+ fill();
+ }
+
+ len = (int) Math.min(len, (buflen == remaining + bufpos ? buflen - 4 : buflen) - bufpos);
+
+ System.arraycopy(buffer, bufpos, b, off, len);
+
+ bufpos += len;
+ remaining -= len;
+
+ return len;
+ }
+ }
+public ServerPacket(InputStream is, Cipher cipher) throws java.io.IOException {
+ packetLength = Misc.readInt(is);
+ paddingLength = 8 - (packetLength % 8);
+ pis = new PacketInputStream(is, packetLength + paddingLength, cipher);
+ Misc.skipFully(pis, paddingLength);
+ packetType = (byte) pis.read();
+}
+public void close(boolean doCrcCheck) throws IOException {
+ pis.close(doCrcCheck);
+}
+public InputStream getInputStream() {
+ return pis;
+}
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/.classpath b/bundles/org.eclipse.team.cvs.ui/.classpath
new file mode 100644
index 000000000..ff1482f4c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/.classpath
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtime.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtimesrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.apache.xerces/xerces.jar" sourcepath="ECLIPSE_HOME/plugins/org.apache.xerces/xercessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resources.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resourcessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.ui/workbench.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.ui/workbenchsrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.help/help.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.help/helpsrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.swt/swt.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.swt/$ws$/swtsrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.boot/boot.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.boot/bootsrc.zip"/>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="/org.eclipse.team.core"/>
+ <classpathentry kind="src" path="/org.eclipse.team.ui"/>
+ <classpathentry kind="src" path="/org.eclipse.team.cvs.core"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.team.cvs.ui/.cvsignore b/bundles/org.eclipse.team.cvs.ui/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/.vcm_meta b/bundles/org.eclipse.team.cvs.ui/.vcm_meta
new file mode 100644
index 000000000..1740e851b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/.vcm_meta
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <nature id="org.eclipse.pde.PluginNature"/>
+ <reference project-name="org.eclipse.team.cvs.core"/>
+ <reference project-name="org.eclipse.team.ui"/>
+ <reference project-name="org.eclipse.team.core"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+ <builder name="org.eclipse.pde.ManifestBuilder">
+ </builder>
+ <builder name="org.eclipse.pde.SchemaBuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.team.cvs.ui/about.html b/bundles/org.eclipse.team.cvs.ui/about.html
index f3b17d947..9a15e5ca2 100644
--- a/bundles/org.eclipse.team.cvs.ui/about.html
+++ b/bundles/org.eclipse.team.cvs.ui/about.html
@@ -1,57 +1,30 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD><TITLE>About</TITLE>
-<META http-equiv=Content-Type content="text/html; charset=windows-1252">
-<STYLE type=text/css>P {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TABLE {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TD {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TH {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-PRE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-H2 {
- FONT-WEIGHT: bold; FONT-SIZE: 18pt; LINE-HEIGHT: 14px; FONT-FAMILY: arial, helvetica, geneva
-}
-CODE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-SUP {
- FONT-SIZE: 10px; FONT-FAMILY: arial,helvetica,geneva
-}
-H3 {
- FONT-WEIGHT: bold; FONT-SIZE: 14pt; FONT-FAMILY: arial, helvetica, geneva
-}
-LI {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-H1 {
- FONT-WEIGHT: bold; FONT-SIZE: 28px; FONT-FAMILY: arial, helvetica, geneva
-}
-BODY {
- MARGIN-TOP: 5mm; FONT-SIZE: 10pt; MARGIN-LEFT: 3mm; FONT-FAMILY: arial, helvetica, geneva
-}
-</STYLE>
-
-<META content="MSHTML 5.50.4522.1800" name=GENERATOR></HEAD>
-<BODY lang=EN-US vLink=purple link=blue>
-<TABLE cellSpacing=5 cellPadding=2 width="100%" border=0>
- <TBODY>
- <TR>
- <TD vAlign=top align=left bgColor=#0080c0 colSpan=2><B><FONT
- face=Arial,Helvetica color=#ffffff>About This Plug-in</FONT></B></TD></TR>
- <TR>
- <TD>
- <P>1st November, 2001</P>
- <H3>License</H3>
- <P>Eclipse.org makes available all content in this plug-in. The plug-in is
- provided to you under the terms and conditions of the <A
- href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License
- Version 0.5</A>. For purposes of the Common Public License, "Program" will
- mean the plug-in.</P></TD></TR></TBODY></TABLE></BODY></HTML>
+<html>
+<head>
+<title>About</title>
+<style type="text/css">
+p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
+pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code { font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup { font-family: arial,helvetica,geneva; font-size: 10px}
+h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li { font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
+</style>
+</head>
+<body>
+<body lang=EN-US link=blue vlink=purple>
+<table border=0 cellspacing=5 cellpadding=2 width="100%" >
+ <tr>
+ <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF" face="Arial,Helvetica">About This Plug-in</font></b></td>
+ </tr>
+ <tr>
+ <td>
+<p>1st November, 2001</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in. The plug-in is provided to you under the terms and conditions of the
+<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a>. For purposes of the Common Public License, &quot;Program&quot; will mean the plug-in.</p>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/build.properties b/bundles/org.eclipse.team.cvs.ui/build.properties
new file mode 100644
index 000000000..ebd88cd76
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/build.properties
@@ -0,0 +1,3 @@
+# Eclipse build contribution
+source.teamcvsui.jar=src/
+bin.includes=about.html,plugin.xml,plugin.properties,*.jar \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/clcl16/refresh.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/clcl16/refresh.gif
new file mode 100644
index 000000000..a063c230a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/clcl16/refresh.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/ctool16/checkout.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/ctool16/checkout.gif
new file mode 100644
index 000000000..012832b00
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/ctool16/checkout.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/console_view.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/console_view.gif
new file mode 100644
index 000000000..e6d5b138d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/console_view.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/repo_rep.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/repo_rep.gif
new file mode 100644
index 000000000..6c8fd5f18
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/cview16/repo_rep.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/obj16/repository_rep.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/obj16/repository_rep.gif
new file mode 100644
index 000000000..012832b00
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/obj16/repository_rep.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newconnect_wiz.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newconnect_wiz.gif
new file mode 100644
index 000000000..f3d2a4d59
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newconnect_wiz.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newlocation_wiz.gif b/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newlocation_wiz.gif
new file mode 100644
index 000000000..b0f400d15
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/icons/full/wizards/newlocation_wiz.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/plugin.properties b/bundles/org.eclipse.team.cvs.ui/plugin.properties
new file mode 100644
index 000000000..5bdf3a44c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/plugin.properties
@@ -0,0 +1,39 @@
+pluginName=CVS UI Plugin
+
+ConfigurationWizard.name=CVS
+
+AddAction.label=Add
+AddAction.tooltip=Add
+
+TagAction.label=Tag...
+TagAction.tooltip=Tag
+
+DiffAction.label=Base
+DiffAction.tooltip=Compare with Base Content on CVS Server
+
+UpdateAction.label=Update
+UpdateAction.tooltip=Update
+
+ReplaceWithAction.label=Remote Contents
+ReplaceWithAction.tooltip=Replace with Content on CVS Server
+
+CommitAction.label=Commit...
+CommitAction.tooltip=Commit
+
+FixTypeAction.label=Fix Type and Update
+FixTypeAction.tooltip=Fix Type and Update
+
+AddToWorkspaceAction.label=Add to Workspace
+AddToWorkspaceAction.tooltip=Add to Workspace
+
+OpenRemoteFileAction.label=Open
+OpenRemoteFileAction.tooltip=Open Remote File
+
+CheckoutAction.label=Check Out Project
+CheckoutAction.tooltip=Check out a module from a CVS repository
+
+viewCategory=&CVS
+
+Console.name=CVS Console
+
+RepositoriesView.name=CVS Repositories
diff --git a/bundles/org.eclipse.team.cvs.ui/plugin.xml b/bundles/org.eclipse.team.cvs.ui/plugin.xml
new file mode 100644
index 000000000..5dfc66aa3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/plugin.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ name="%pluginName"
+ id="org.eclipse.team.cvs.ui"
+ version="2.0.0"
+ provider-name="Object Technology International, Inc."
+ class="org.eclipse.team.internal.ccvs.ui.CVSUIPlugin">
+
+ <requires>
+ <import plugin="org.eclipse.core.runtime"/>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.ui"/>
+ <import plugin="org.eclipse.team.core"/>
+ <import plugin="org.eclipse.team.ui"/>
+ <import plugin="org.eclipse.team.cvs.core"/>
+ </requires>
+
+ <runtime>
+ <library name="teamcvsui.jar"/>
+ </runtime>
+
+ <!-- *************** Authenticator **************** -->
+ <extension point="org.eclipse.team.cvs.core.authenticator">
+ <authenticator>
+ <run class="org.eclipse.team.internal.ccvs.ui.WorkbenchUserAuthenticator"/>
+ </authenticator>
+ </extension>
+
+ <!-- *************** Decorator **************** -->
+ <extension point="org.eclipse.team.ui.decorators">
+ <decorator
+ class="org.eclipse.team.internal.ccvs.ui.CVSDecorator"
+ natureId="org.eclipse.team.cvs.core.cvsnature"/>
+ </extension>
+
+
+ <!-- ******************* Wizards ****************** -->
+ <extension point="org.eclipse.team.ui.configurationWizards">
+ <wizard
+ name="%ConfigurationWizard.name"
+ icon="icons/full/wizards/newconnect_wiz.gif"
+ class="org.eclipse.team.internal.ccvs.ui.wizards.ConfigurationWizard"
+ id="org.eclipse.team.ccvs.ui.ConfigurationWizard">
+ </wizard>
+ </extension>
+
+ <!-- ************** Property Pages *************** -->
+ <extension point="org.eclipse.ui.propertyPages">
+ <page name="CVS" id="org.eclipse.team.ccvs.ui.propertyPages.CVSPropertiesPage"
+ objectClass="org.eclipse.core.resources.IProject"
+ class="org.eclipse.team.internal.ccvs.ui.CVSPropertiesPage">
+ <filter name="nature" value="org.eclipse.team.cvs.core.cvsnature"/>
+ </page>
+ </extension>
+
+ <!-- ******************* Menus ******************** -->
+ <extension point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ id="org.eclipse.team.ccvs.ui.ResourceContributions"
+ objectClass="org.eclipse.core.resources.IResource">
+ <filter name="projectNature" value="org.eclipse.team.cvs.core.cvsnature"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.add"
+ label="%AddAction.label"
+ tooltip="%AddAction.tooltip"
+ menubarPath="team.main/checkinGroup"
+ class="org.eclipse.team.internal.ccvs.ui.actions.AddAction"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.tag"
+ label="%TagAction.label"
+ tooltip="%TagAction.tooltip"
+ menubarPath="team.main/checkinGroup"
+ class="org.eclipse.team.internal.ccvs.ui.actions.TagAction"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.diff"
+ label="%DiffAction.label"
+ tooltip="%DiffAction.tooltip"
+ menubarPath="compareWithMenu/compareWithGroup"
+ class="org.eclipse.team.internal.ccvs.ui.actions.DiffAction"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.update"
+ label="%UpdateAction.label"
+ tooltip="%UpdateAction.tooltip"
+ menubarPath="team.main/checkinGroup"
+ class="org.eclipse.team.internal.ccvs.ui.actions.UpdateAction"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.replace"
+ label="%ReplaceWithAction.label"
+ tooltip="%ReplaceWithAction.tooltip"
+ menubarPath="replaceWithMenu/replaceWithGroup"
+ class="org.eclipse.team.ui.actions.UpdateAction"/>
+ <action
+ id="org.eclipse.team.ccvs.ui.commit"
+ label="%CommitAction.label"
+ tooltip="%CommitAction.tooltip"
+ menubarPath="team.main/checkinGroup"
+ class="org.eclipse.team.internal.ccvs.ui.actions.CommitAction"/>
+ </objectContribution>
+ <objectContribution
+ id="org.eclipse.team.ccvs.ui.ResourceContributions"
+ objectClass="org.eclipse.team.ccvs.core.IRemoteFolder">
+ <action
+ id="org.eclipse.team.ccvs.ui.addToWorkspace"
+ label="%AddToWorkspaceAction.label"
+ tooltip="%AddToWorkspaceAction.tooltip"
+ class="org.eclipse.team.internal.ccvs.ui.actions.AddToWorkspaceAction"/>
+ </objectContribution>
+ <objectContribution
+ id="org.eclipse.team.ccvs.ui.ResourceContributions"
+ objectClass="org.eclipse.team.ccvs.core.IRemoteFile">
+ <action
+ id="org.eclipse.team.ccvs.ui.openRemoteFile"
+ label="%OpenRemoteFileAction.label"
+ tooltip="%OpenRemoteFileAction.tooltip"
+ class="org.eclipse.team.internal.ccvs.ui.actions.OpenRemoteFileAction"/>
+ </objectContribution>
+ </extension>
+
+ <!-- ************** Action Set ***************** -->
+ <extension point="org.eclipse.ui.actionSets">
+ <actionSet id="org.eclipse.team.ccvs.CVSActionSet" label="CVS" visible="true" description="CVS specific actions">
+ <action id="org.eclipse.team.ccvs.Checkout"
+ toolbarPath="Normal/CVSWizards"
+ label="%CheckoutAction.label"
+ tooltip="%CheckoutAction.tooltip"
+ icon="icons/full/ctool16/checkout.gif"
+ class="org.eclipse.team.internal.ccvs.ui.actions.CheckoutAction"/>
+ </actionSet>
+ </extension>
+
+ <!-- ************* Property pages ************** -->
+<!--
+ <extension point="org.eclipse.ui.propertyPages">
+ <page name="CVS"
+ id="org.eclipse.team.ccvs.ui.ResourcePropertiesPage"
+ objectClass="org.eclipse.core.resources.IResource"
+ class="org.eclipse.team.internal ccvs.ui.ResourcePropertiesPage">
+ <filter name="projectNature" value="org.eclipse.team.ccvs.cvsnature"/>
+ </page>
+ </extension>
+-->
+
+ <!-- ************** Views ********************** -->
+ <extension point="org.eclipse.ui.views">
+ <category
+ id="org.eclipse.team.ccvs.ui"
+ name="%viewCategory">
+ </category>
+ <view
+ name="%Console.name"
+ category="org.eclipse.team.ccvs.ui"
+ class="org.eclipse.team.internal.ccvs.ui.Console"
+ id="org.eclipse.team.ccvs.ui.console"
+ icon="icons/full/cview16/console_view.gif">
+ </view>
+ <view
+ id="org.eclipse.team.ccvs.ui.RepositoriesView"
+ icon="icons/full/cview16/repo_rep.gif"
+ name="%RepositoriesView.name"
+ category="org.eclipse.team.ccvs.ui"
+ class="org.eclipse.team.internal.ccvs.ui.RepositoriesView">
+ </view>
+ </extension>
+
+ <extension point="org.eclipse.ui.resourceFilters">
+ <filter pattern="CVS" selected="true"/>
+ </extension>
+</plugin>
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/AdaptableList.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/AdaptableList.java
new file mode 100644
index 000000000..92177eb62
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/AdaptableList.java
@@ -0,0 +1,88 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.ui.internal.model.WorkbenchAdapter;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * A list of adaptable objects. This is a generic list that can
+ * be used to display an arbitrary set of adaptable objects in the workbench.
+ * Also implements the IWorkbenchAdapter interface for simple display
+ * and navigation.
+ */
+public class AdaptableList extends WorkbenchAdapter implements IAdaptable {
+ protected List children = new ArrayList();
+ /**
+ * Creates a new adaptable list with the given children.
+ */
+ public AdaptableList() {
+ }
+ /**
+ * Creates a new adaptable list with the given children.
+ */
+ public AdaptableList(IAdaptable[] newChildren) {
+ for (int i = 0; i < newChildren.length; i++) {
+ children.add(newChildren[i]);
+ }
+ }
+ /**
+ * Adds all the adaptable objects in the given enumeration to this list.
+ * Returns this list.
+ */
+ public AdaptableList add(Iterator e) {
+ while (e.hasNext()) {
+ add((IAdaptable)e.next());
+ }
+ return this;
+ }
+ /**
+ * Adds the given adaptable object to this list. Returns this list.
+ */
+ public AdaptableList add(IAdaptable a) {
+ children.add(a);
+ return this;
+ }
+ /**
+ * Returns an object which is an instance of the given class
+ * associated with this object. Returns <code>null</code> if
+ * no such object can be found.
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) return this;
+ return null;
+ }
+ /**
+ * Returns the elements in this list.
+ */
+ public Object[] getChildren() {
+ return children.toArray();
+ }
+ /**
+ * Returns the elements in this list.
+ * @see IWorkbenchAdapter#getChildren
+ */
+ public Object[] getChildren(Object o) {
+ return children.toArray();
+ }
+ /**
+ * Adds the given adaptable object to this list.
+ */
+ public void remove(IAdaptable a) {
+ children.remove(a);
+ }
+ /**
+ * Returns the number of items in the list
+ */
+ public int size() {
+ return children.size();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
new file mode 100644
index 000000000..7deeb800c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
@@ -0,0 +1,92 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.Client;
+import org.eclipse.team.internal.ccvs.core.resources.api.FileProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.FolderProperties;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedFolder;
+import org.eclipse.team.internal.ccvs.core.resources.api.IManagedResource;
+import org.eclipse.team.ui.ISharedImages;
+import org.eclipse.team.ui.ITeamDecorator;
+import org.eclipse.team.ui.TeamUIPlugin;
+
+public class CVSDecorator implements ITeamDecorator {
+
+ List listeners = new ArrayList(3);
+ private static final CoreException CORE_EXCEPTION = new CoreException(new Status(IStatus.OK, "id", 1, "", null));
+
+ public CVSDecorator() {
+ }
+
+ /*
+ * @see ITeamDecorator#getText(String, IResource)
+ */
+ public String getText(String text, IResource resource) {
+ ITeamProvider p = TeamPlugin.getManager().getProvider(resource);
+ if (p == null) {
+ return text;
+ }
+
+ try {
+ switch (resource.getType()) {
+ case IResource.PROJECT:
+ IManagedFolder project = Client.getManagedFolder(resource.getLocation().toFile());
+ FolderProperties folderInfo = project.getFolderInfo();
+ return Policy.bind("CVSDecorator.projectDecoration", text, folderInfo.getRoot());
+ case IResource.FILE:
+ IManagedResource file = Client.getManagedResource(resource.getLocation().toFile());
+ FileProperties fileInfo = file.getParent().getFile(resource.getName()).getFileInfo();
+ String tag = "";
+ if (file.isManaged()) {
+ tag = fileInfo.getTag();
+ } else {
+ return text;
+ }
+ if (tag.equals("")) {
+ return Policy.bind("CVSDecorator.fileDecorationNoTag", text, fileInfo.getVersion());
+ } else {
+ return Policy.bind("CVSDecorator.fileDecorationWithTag", new Object[] {text, tag, fileInfo.getVersion()});
+ }
+ }
+ } catch (CVSException e) {
+ return text;
+ }
+ return text;
+ }
+
+ /*
+ * @see ITeamDecorator#getImage(IResource)
+ */
+ public ImageDescriptor[][] getImage(IResource resource) {
+ List overlays = new ArrayList(5);
+ CVSTeamProvider p = (CVSTeamProvider)TeamPlugin.getManager().getProvider(resource);
+ if(p!=null) {
+ if(p.isDirty(resource)) {
+ overlays.add(TeamUIPlugin.getPlugin().getImageDescriptor(ISharedImages.IMG_DIRTY_OVR));
+ }
+ if(p.hasRemote(resource)) {
+ overlays.add(TeamUIPlugin.getPlugin().getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR));
+ }
+ if(p.isCheckedOut(resource)) {
+ overlays.add(TeamUIPlugin.getPlugin().getImageDescriptor(ISharedImages.IMG_CHECKEDOUT_OVR));
+ }
+ }
+ return new ImageDescriptor[][] {(ImageDescriptor[])overlays.toArray(new ImageDescriptor[overlays.size()])};
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSOperationCancelledException.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSOperationCancelledException.java
new file mode 100644
index 000000000..d775b1e74
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSOperationCancelledException.java
@@ -0,0 +1,31 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+
+public class CVSOperationCancelledException extends CVSException {
+ /**
+ * Constructor for CVSOperationCancelledException
+ */
+ public CVSOperationCancelledException() {
+ this(new CVSStatus(IStatus.INFO, Policy.bind("CVSOperationCancelledException.operationCancelled")));
+ }
+ /**
+ * Constructor for CVSOperationCancelledException
+ */
+ public CVSOperationCancelledException(String message) {
+ this(new CVSStatus(IStatus.INFO, message));
+ }
+ /**
+ * Constructor for CVSOperationCancelledException
+ */
+ public CVSOperationCancelledException(IStatus status) {
+ super(status);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java
new file mode 100644
index 000000000..2bccf0032
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPropertiesPage.java
@@ -0,0 +1,195 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.IUserInfo;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+public class CVSPropertiesPage extends PropertyPage {
+ IProject project;
+
+ // Widgets
+ Text userText;
+ Text passwordText;
+ Combo methodType;
+
+ boolean passwordChanged;
+
+ IUserInfo info;
+ CVSTeamProvider provider;
+
+ /*
+ * @see PreferencesPage#createContents
+ */
+ protected Control createContents(Composite parent) {
+ initialize();
+
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ composite.setLayout(layout);
+
+ Label label = createLabel(composite, Policy.bind("CVSPropertiesPage.connectionType"));
+ methodType = createCombo(composite);
+
+ label = createLabel(composite, Policy.bind("CVSPropertiesPage.user"));
+ userText = createTextField(composite);
+
+ label = createLabel(composite, Policy.bind("CVSPropertiesPage.password"));
+ passwordText = createTextField(composite);
+ passwordText.setEchoChar('*');
+
+ initializeValues();
+ passwordText.addListener(SWT.Modify, new Listener() {
+ public void handleEvent(Event event) {
+ passwordChanged = true;
+ }
+ });
+ return composite;
+ }
+ /**
+ * Utility method that creates a combo box
+ *
+ * @param parent the parent for the new label
+ * @return the new widget
+ */
+ protected Combo createCombo(Composite parent) {
+ Combo combo = new Combo(parent, SWT.READ_ONLY);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ combo.setLayoutData(data);
+ return combo;
+ }
+ /**
+ * Utility method that creates a label instance
+ * and sets the default layout data.
+ *
+ * @param parent the parent for the new label
+ * @param text the text for the new label
+ * @return the new label
+ */
+ protected Label createLabel(Composite parent, String text) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText(text);
+ GridData data = new GridData();
+ data.horizontalSpan = 1;
+ data.horizontalAlignment = GridData.FILL;
+ label.setLayoutData(data);
+ return label;
+ }
+ /**
+ * Create a text field specific for this application
+ *
+ * @param parent the parent of the new text field
+ * @return the new text field
+ */
+ protected Text createTextField(Composite parent) {
+ Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.verticalAlignment = GridData.CENTER;
+ data.grabExcessVerticalSpace = false;
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ text.setLayoutData(data);
+ return text;
+ }
+ /**
+ * Initializes the page
+ */
+ private void initialize() {
+ // Get the project that is the source of this property page
+ project = null;
+ IAdaptable element = getElement();
+ if (element instanceof IProject) {
+ project = (IProject)element;
+ } else {
+ Object adapter = element.getAdapter(IProject.class);
+ if (adapter instanceof IProject) {
+ project = (IProject)adapter;
+ }
+ }
+ }
+ /**
+ * Set the initial values of the widgets
+ */
+ private void initializeValues() {
+ passwordChanged = false;
+
+ provider = (CVSTeamProvider)TeamPlugin.getManager().getProvider(project);
+ if (provider == null) return;
+
+ String[] methods = CVSTeamProvider.getConnectionMethods();
+ for (int i = 0; i < methods.length; i++) {
+ methodType.add(methods[i]);
+ }
+ try {
+ String method = provider.getConnectionMethod(project);
+ methodType.select(methodType.indexOf(method));
+
+ info = provider.getUserInfo(project);
+ userText.setText(info.getUsername());
+ } catch (TeamException e) {
+ showError(e.getStatus());
+ }
+ passwordText.setText("*********");
+ }
+ /*
+ * @see PreferencesPage#performOk
+ */
+ public boolean performOk() {
+ info.setUsername(userText.getText());
+ if (passwordChanged) {
+ info.setPassword(passwordText.getText());
+ }
+ try {
+ provider.setConnectionInfo(project, methodType.getText(), info);
+ } catch (TeamException e) {
+ showError(e.getStatus());
+ }
+ return true;
+ }
+ /**
+ * Shows the given errors to the user.
+ */
+ protected void showError(IStatus status) {
+ showError(status, null, null, getShell());
+ }
+ protected void showError(IStatus status, String title, String message, Shell shell) {
+ if (!status.isOK()) {
+ IStatus toShow = status;
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ if (children.length == 1) {
+ toShow = children[0];
+ }
+ }
+ if (title == null)
+ title = status.getMessage();
+ ErrorDialog.openError(shell, title, message, toShow);
+ CVSUIPlugin.log(toShow);
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
new file mode 100644
index 000000000..145b15ae5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
@@ -0,0 +1,135 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Hashtable;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+import org.eclipse.team.internal.ccvs.ui.model.CVSAdapterFactory;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * UI Plugin for CVS provider-specific workbench functionality.
+ */
+public class CVSUIPlugin extends AbstractUIPlugin {
+ /**
+ * The id of the CVS plug-in
+ */
+ public static final String ID = "org.eclipse.team.cvs.ui";
+
+ private Hashtable imageDescriptors = new Hashtable(20);
+
+ public final static String ICON_PATH;
+ static {
+ if (Display.getCurrent().getIconDepth() > 4) {
+ ICON_PATH = ICVSUIConstants.ICON_PATH_FULL;
+ } else {
+ ICON_PATH = ICVSUIConstants.ICON_PATH_BASIC;
+ }
+ }
+
+ /**
+ * The singleton plug-in instance
+ */
+ private static CVSUIPlugin plugin;
+
+ /**
+ * CVSUIPlugin constructor
+ *
+ * @param descriptor the plugin descriptor
+ */
+ public CVSUIPlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+ plugin = this;
+ }
+
+ /**
+ * Creates an image and places it in the image registry.
+ */
+ protected void createImageDescriptor(String id, URL baseURL) {
+ URL url = null;
+ try {
+ url = new URL(baseURL, ICON_PATH + id);
+ } catch (MalformedURLException e) {
+ }
+ ImageDescriptor desc = ImageDescriptor.createFromURL(url);
+ imageDescriptors.put(id, desc);
+ }
+
+ /**
+ * Returns the active workbench page.
+ *
+ * @return the active workbench page
+ */
+ public static IWorkbenchPage getActivePage() {
+ return getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ }
+
+ /**
+ * Returns the image descriptor for the given image ID.
+ * Returns null if there is no such image.
+ */
+ public ImageDescriptor getImageDescriptor(String id) {
+ return (ImageDescriptor)imageDescriptors.get(id);
+ }
+
+ /**
+ * Returns the singleton plug-in instance.
+ *
+ * @return the plugin instance
+ */
+ public static CVSUIPlugin getPlugin() {
+ return plugin;
+ }
+
+ /**
+ * Initializes the table of images used in this plugin.
+ */
+ private void initializeImages() {
+ URL baseURL = getDescriptor().getInstallURL();
+
+ // objects
+ createImageDescriptor(ICVSUIConstants.IMG_REPOSITORY, baseURL);
+ createImageDescriptor(ICVSUIConstants.IMG_REFRESH, baseURL);
+ createImageDescriptor(ICVSUIConstants.IMG_NEWLOCATION, baseURL);
+ }
+ /**
+ * Convenience method for logging statuses to the plugin log
+ *
+ * @param status the status to log
+ */
+ public static void log(IStatus status) {
+ getPlugin().getLog().log(status);
+ }
+
+ /**
+ * @see Plugin#startup()
+ */
+ public void startup() throws CoreException {
+ Policy.localize("org.eclipse.team.internal.ccvs.ui.messages");
+ CVSTeamProvider.setPrintStream(new PrintStream(new ConsoleOutputStream()));
+
+ CVSAdapterFactory factory = new CVSAdapterFactory();
+ Platform.getAdapterManager().registerAdapters(factory, IRemoteFile.class);
+ Platform.getAdapterManager().registerAdapters(factory, IRemoteFolder.class);
+ Platform.getAdapterManager().registerAdapters(factory, IRemoteRoot.class);
+
+ initializeImages();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Console.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Console.java
new file mode 100644
index 000000000..ac49d922f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Console.java
@@ -0,0 +1,186 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Vector;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.TextViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * Console is a view that displays the communication with the CVS server
+ */
+public class Console extends ViewPart {
+
+ public final static String CONSOLE_ID = "org.eclipse.team.ccvs.ui.console";
+
+ private TextViewer viewer;
+ private Action copyAction;
+ private Action selectAllAction;
+ private Action clearOutputAction;
+ private IDocument document;
+
+ // All instances of the console
+ static Vector instances = new Vector();
+
+ /**
+ * Console Constructor
+ */
+ public Console() {
+ instances.add(this);
+ document = new Document();
+ }
+
+ /**
+ * Returns all instances of the console view
+ *
+ * @return all instances of the console
+ */
+ public static Console[] getInstances() {
+ return (Console[])instances.toArray(new Console[instances.size()]);
+ }
+
+ /**
+ * Create contributed actions
+ */
+ protected void createActions() {
+ copyAction = new Action(Policy.bind("Console.copy")) {
+ public void run() {
+ viewer.doOperation(viewer.COPY);
+ }
+ };
+
+ selectAllAction = new Action(Policy.bind("Console.selectAll")) {
+ public void run() {
+ viewer.doOperation(viewer.SELECT_ALL);
+ }
+ };
+
+ clearOutputAction = new Action(Policy.bind("Console.clearOutput")) {
+ public void run() {
+ clearOutput();
+ }
+ };
+
+ MenuManager mgr = new MenuManager();
+ mgr.setRemoveAllWhenShown(true);
+ mgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mgr) {
+ fillContextMenu(mgr);
+ }
+ });
+ Menu menu = mgr.createContextMenu(viewer.getControl());
+ viewer.getControl().setMenu(menu);
+ }
+
+ /**
+ * Append the given string to the console
+ *
+ * @param message the string to append
+ */
+ public void append(final String message) {
+ getViewSite().getShell().getDisplay().syncExec(new Runnable() {
+ public void run() {
+ document.set(document.get() + message);
+ if (message.length() > 0 && viewer != null)
+ viewer.revealRange(document.get().length() - 1, 1);
+ }
+ });
+ }
+
+ /**
+ * Append the given string to all consoles
+ *
+ * @param message the string to append
+ */
+ public static void appendAll(String message) {
+ // Show the console
+ try {
+ CVSUIPlugin.getActivePage().showView(CONSOLE_ID);
+ } catch (PartInitException e) {
+ CVSUIPlugin.log(e.getStatus());
+ }
+
+ if (message.length() == 0) return;
+ boolean appendCr = message.charAt(message.length() - 1) == '\n';
+ Console[] consoles = Console.getInstances();
+ for (int i = 0; i < consoles.length; i++) {
+ consoles[i].append(message);
+ if (appendCr) {
+ consoles[i].append("\n");
+ }
+ }
+ }
+
+ /**
+ * Clear the output of the console
+ */
+ public void clearOutput() {
+ document.set("");
+ }
+
+ /**
+ * Add the actions to the context menu
+ *
+ * @param manager the manager of the context menu
+ */
+ protected void fillContextMenu(IMenuManager manager) {
+ copyAction.setEnabled(viewer.canDoOperation(viewer.COPY));
+ selectAllAction.setEnabled(viewer.canDoOperation(viewer.SELECT_ALL));
+ manager.add(copyAction);
+ manager.add(selectAllAction);
+ manager.add(new Separator());
+ manager.add(clearOutputAction);
+ }
+
+ /*
+ * @see WorkbenchPart#setFocus()
+ */
+ public void setFocus() {
+ viewer.getTextWidget().setFocus();
+ }
+
+ /*
+ * @see WorkbenchPart#createPartControl(Composite)
+ */
+ public void createPartControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
+
+ viewer = new TextViewer(composite, SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ viewer.setEditable(false);
+ viewer.getControl().setLayoutData(data);
+ viewer.setDocument(document);
+
+ createActions();
+ }
+
+ /*
+ * @see WorkbenchPart#dispose
+ */
+ public void dispose() {
+ instances.remove(this);
+ super.dispose();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ConsoleOutputStream.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ConsoleOutputStream.java
new file mode 100644
index 000000000..71f5924f1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ConsoleOutputStream.java
@@ -0,0 +1,39 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * ConsoleOutputStream is a buffered output stream that additionally
+ * flushes the buffer when a carriage return is found.
+ */
+public class ConsoleOutputStream extends OutputStream {
+
+ private static final int BUFFER_LENGTH = 256;
+ private static byte[] buffer = new byte[BUFFER_LENGTH];
+ private static int size = 0;
+
+ /**
+ * @see OutputStream#write(int)
+ */
+ public void write(int b) throws IOException {
+ if ((((char)b) == '\n') || (size >= buffer.length))
+ writeBufferToConsole();
+ else
+ buffer[size++] = (byte)b;
+ }
+
+ /**
+ * Flush the buffer
+ */
+ private void writeBufferToConsole() {
+ Console.appendAll(new String(buffer, 0, size));
+ size = 0;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ICVSUIConstants.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ICVSUIConstants.java
new file mode 100644
index 000000000..4d24bc76c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ICVSUIConstants.java
@@ -0,0 +1,24 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+public interface ICVSUIConstants {
+ // image paths
+ public final String ICON_PATH_FULL = "icons/full/";
+ public final String ICON_PATH_BASIC = "icons/basic/";
+
+ // images
+
+ // objects
+ public final String IMG_REPOSITORY = "obj16/repository_rep.gif";
+
+ // toolbar
+ public final String IMG_REFRESH = "clcl16/refresh.gif";
+
+ // wizards
+ public final String IMG_NEWLOCATION = "wizards/newlocation_wiz.gif";
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Policy.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Policy.java
new file mode 100644
index 000000000..2503e8d85
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/Policy.java
@@ -0,0 +1,83 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+public class Policy {
+ protected static ResourceBundle bundle = null;
+
+ /**
+ * Creates a NLS catalog for the given locale.
+ */
+ public static void localize(String bundleName) {
+ bundle = ResourceBundle.getBundle(bundleName);
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given string.
+ */
+ public static String bind(String id, String binding) {
+ return bind(id, new String[] { binding });
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given strings.
+ */
+ public static String bind(String id, String binding1, String binding2) {
+ return bind(id, new String[] { binding1, binding2 });
+ }
+
+ /**
+ * Gets a string from the resource bundle. We don't want to crash because of a missing String.
+ * Returns the key if not found.
+ */
+ public static String bind(String key) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and binds it with the given arguments. If the key is
+ * not found, return the key.
+ */
+ public static String bind(String key, Object[] args) {
+ try {
+ return MessageFormat.format(bind(key), args);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Progress monitor helpers
+ */
+ public static void checkCanceled(IProgressMonitor monitor) {
+ if (monitor.isCanceled())
+ throw new OperationCanceledException();
+ }
+ public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+ if (monitor == null)
+ return new NullProgressMonitor();
+ return monitor;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java
new file mode 100644
index 000000000..fc2f700c0
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ReleaseCommentDialog.java
@@ -0,0 +1,91 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Prompts the user for a multi-line comment for releasing to CVS.
+ */
+public class ReleaseCommentDialog extends Dialog {
+ private static final int WIDTH_HINT = 350;
+ private static final int HEIGHT_HINT = 50;
+
+ private String comment = "";
+
+ private Text text;
+
+ /**
+ * ReleaseCommentDialog constructor.
+ *
+ * @param parentShell the parent of this dialog
+ */
+ public ReleaseCommentDialog(Shell parentShell) {
+ super(parentShell);
+ }
+ /*
+ * @see Dialog#createDialogArea(Composite)
+ */
+ protected Control createDialogArea(Composite parent) {
+ getShell().setText(Policy.bind("ReleaseCommentDialog.title"));
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label label = new Label(composite, SWT.NULL);
+ label.setLayoutData(new GridData());
+ label.setText(Policy.bind("ReleaseCommentDialog.enterComment"));
+
+ text = new Text(composite, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.widthHint = WIDTH_HINT;
+ data.heightHint = HEIGHT_HINT;
+ text.setLayoutData(data);
+ text.setText(comment);
+ text.selectAll();
+/* text.addListener(SWT.KeyDown, new Listener() {
+ public void handleEvent(Event e) {
+ if (((e.stateMask & SWT.CTRL) != 0) && (e.character == '\n')) {
+ okPressed();
+ }
+ }
+ });*/
+ return composite;
+ }
+ /**
+ * Return the entered comment
+ *
+ * @return the comment
+ */
+ public String getComment() {
+ return comment;
+ }
+ /**
+ * Set the initial comment
+ *
+ * @param comment the initial comment
+ */
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+ /*
+ * @see Dialog#okPressed
+ */
+ protected void okPressed() {
+ comment = text.getText();
+ super.okPressed();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileEditorInput.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileEditorInput.java
new file mode 100644
index 000000000..76b16bcde
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileEditorInput.java
@@ -0,0 +1,191 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.ui.IPersistableElement;
+import org.eclipse.ui.IStorageEditorInput;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * An editor input for a file in a repository.
+ */
+public class RemoteFileEditorInput implements IWorkbenchAdapter, IStorageEditorInput {
+ private IRemoteFile file;
+ private IStorage storage;
+
+ /**
+ * Creates FileEditionEditorInput on the given file.
+ */
+ public RemoteFileEditorInput(IRemoteFile file) {
+ this.file = file;
+ }
+ /**
+ * Returns whether the editor input exists.
+ * <p>
+ * This method is primarily used to determine if an editor input should
+ * appear in the "File Most Recently Used" menu. An editor input will appear
+ * in the list until the return value of <code>exists</code> becomes
+ * <code>false</code> or it drops off the bottom of the list.
+ *
+ * @return <code>true</code> if the editor input exists; <code>false</code>
+ * otherwise
+ */
+ public boolean exists() {
+ return true;
+ }
+ public boolean equals(Object o) {
+ if (!(o instanceof RemoteFileEditorInput)) return false;
+ RemoteFileEditorInput input = (RemoteFileEditorInput)o;
+ return file.equals(input.file);
+ }
+ /**
+ * Returns an object which is an instance of the given class
+ * associated with this object. Returns <code>null</code> if
+ * no such object can be found.
+ *
+ * @param adapter the adapter class to look up
+ * @return a object castable to the given class,
+ * or <code>null</code> if this object does not
+ * have an adapter for the given class
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) {
+ return this;
+ }
+ return null;
+ }
+ /**
+ * Returns the children of this object. When this object
+ * is displayed in a tree, the returned objects will be this
+ * element's children. Returns an empty array if this
+ * object has no children.
+ *
+ * @param object The object to get the children for.
+ */
+ public Object[] getChildren(Object o) {
+ return new Object[0];
+ }
+ /**
+ * Returns an open input stream on the contents of this file.
+ * The client is responsible for closing the stream when finished.
+ *
+ * @return an input stream containing the contents of the file
+ * @exception CoreException if this method fails.
+ */
+ public InputStream getContents() throws CoreException {
+ try {
+ return file.getContents(new NullProgressMonitor());
+ } catch (TeamException e) {
+ throw new CoreException(e.getStatus());
+ }
+ }
+ /**
+ * Returns the content type of the input. For instance, if the input
+ * wraps an <code>IFile</code> the content type would be derived from
+ * the extension or mime type. If the input wraps another object it
+ * may just be the object type. The content type is used for
+ * editor mapping.
+ */
+ public String getContentType() {
+ String name = file.getName();
+ return name.substring(name.lastIndexOf('.')+1);
+ }
+ /**
+ * Returns the fully qualified path name of the input.
+ */
+ public String getFullPath() {
+ //use path to make sure slashes are correct
+ // to do. For now, just return the name
+ //return new Path(file.getProjectName()).append(file.getProjectRelativePath()).toString();
+ return getName();
+ }
+ /**
+ * Returns the image descriptor for this input.
+ *
+ * @return the image descriptor for this input
+ */
+ public ImageDescriptor getImageDescriptor() {
+ IWorkbenchAdapter fileAdapter = (IWorkbenchAdapter)file.getAdapter(IWorkbenchAdapter.class);
+ return fileAdapter == null ? null : fileAdapter.getImageDescriptor(file);
+ }
+ /**
+ * @see IWorkbenchAdapter#getImageDescriptor
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ IWorkbenchAdapter fileAdapter = (IWorkbenchAdapter)file.getAdapter(IWorkbenchAdapter.class);
+ return fileAdapter == null ? null : fileAdapter.getImageDescriptor(file);
+ }
+ /**
+ * @see IWorkbenchAdapter#getLabel
+ */
+ public String getLabel(Object o) {
+ return file.getName();
+ }
+ /**
+ * Returns the input name for display purposes. For instance, if
+ * the fully qualified input name is "a\b\MyFile.gif" the return value for
+ * <code>getName</code> is "MyFile.gif".
+ */
+ public String getName() {
+ // To do: add version information here
+ return file.getName();
+ }
+ /**
+ * Returns the logical parent of the given object in its tree.
+ * Returns <code>null</code> if there is no parent, or if this object doesn't
+ * belong to a tree.
+ *
+ * @param object The object to get the parent for.
+ */
+ public Object getParent(Object o) {
+ return null;
+ }
+ /*
+ * Returns an interface used to persist the object. If the editor input
+ * cannot be persisted this method returns <code>null</code>.
+ */
+ public IPersistableElement getPersistable() {
+ //not persistable
+ return null;
+ }
+ /**
+ * Returns the underlying IStorage object.
+ *
+ * @return an IStorage object.
+ * @exception CoreException if this method fails
+ */
+ public IStorage getStorage() throws CoreException {
+ if (storage == null) {
+ storage = new RemoteFileStorage(file);
+ }
+ return storage;
+ }
+ /**
+ * Returns the tool tip text for this editor input. This text
+ * is used to differentiate between two input with the same name.
+ * For instance, MyClass.java in folder X and MyClass.java in folder Y.
+ * <p>
+ * The format of the path will vary with each input type. For instance,
+ * if the editor input is of type <code>IFileEditorInput</code> this method
+ * should return the fully qualified resource path. For editor input of
+ * other types it may be different.
+ * </p>
+ * @return the tool tip text
+ */
+ public String getToolTipText() {
+ //use path to make sure slashes are correct
+ return getName();
+ //return new Path(file.getProjectName()).append(file.getProjectRelativePath()).toString();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileStorage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileStorage.java
new file mode 100644
index 000000000..6f1e9c923
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RemoteFileStorage.java
@@ -0,0 +1,41 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.io.InputStream;
+
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.core.TeamException;
+
+public class RemoteFileStorage extends PlatformObject implements IStorage {
+ IRemoteFile file;
+ public RemoteFileStorage(IRemoteFile file) {
+ this.file = file;
+ }
+ public InputStream getContents() throws CoreException {
+ try {
+ return file.getContents(new NullProgressMonitor());
+ } catch (TeamException e) {
+ throw new CoreException(e.getStatus());
+ }
+ }
+ public IPath getFullPath() {
+ return new Path(file.getName());
+ }
+ public String getName() {
+ return file.getName();
+ }
+ public boolean isReadOnly() {
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RepositoriesView.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RepositoriesView.java
new file mode 100644
index 000000000..f480e6cc1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/RepositoriesView.java
@@ -0,0 +1,186 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Properties;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.action.Separator;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.actions.OpenRemoteFileAction;
+import org.eclipse.team.internal.ccvs.ui.model.RemoteContentProvider;
+import org.eclipse.team.internal.ccvs.ui.wizards.ConfigurationWizardMainPage;
+import org.eclipse.team.internal.ccvs.ui.wizards.LocationWizard;
+import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.part.DrillDownAdapter;
+import org.eclipse.ui.part.ViewPart;
+
+/**
+ * RepositoriesView is a view on a set of known CVS repositories
+ * which allows navigation of the structure of the repository and
+ * the performing of CVS-specific operations on the repository contents.
+ */
+public class RepositoriesView extends ViewPart {
+ public static final String VIEW_ID = "org.eclipse.team.ccvs.ui.RepositoriesView";
+
+ // The tree viewer
+ private TreeViewer viewer;
+
+ // The roots
+ private AdaptableList roots;
+
+ private OpenRemoteFileAction openAction;
+
+ // Drill down adapter
+ private DrillDownAdapter drillPart;
+
+ /**
+ * Add a new repository based on the given properties to the viewer.
+ */
+ private void addRepository(Properties properties) {
+ try {
+ roots.add(CVSTeamProvider.getRemoteRoot(properties));
+ if (viewer != null) {
+ viewer.refresh();
+ }
+ } catch (TeamException e) {
+ CVSUIPlugin.log(e.getStatus());
+ }
+ }
+ /**
+ * Contribute actions to the view
+ */
+ private void contributeActions() {
+ // Create actions
+
+ // Refresh (toolbar)
+ final Action refreshAction = new Action(Policy.bind("RepositoriesView.refresh"), CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_REFRESH)) {
+ public void run() {
+ viewer.refresh();
+ }
+ };
+ refreshAction.setToolTipText(Policy.bind("RepositoriesView.refresh"));
+
+ // New Repository (popup)
+ final Action newAction = new Action(Policy.bind("RepositoriesView.new"), CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_NEWLOCATION)) {
+ public void run() {
+ LocationWizard wizard = new LocationWizard();
+ WizardDialog dialog = new WizardDialog(viewer.getTree().getShell(), wizard);
+ int result = dialog.open();
+ if (result == WizardDialog.OK) {
+ ConfigurationWizardMainPage page = (ConfigurationWizardMainPage)dialog.getCurrentPage();
+ Properties properties = page.getProperties();
+ addRepository(properties);
+ }
+ }
+ };
+
+ // Create the popup menu
+ MenuManager menuMgr = new MenuManager();
+ Tree tree = viewer.getTree();
+ Menu menu = menuMgr.createContextMenu(tree);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ // File actions go first (view file)
+ manager.add(new Separator(IWorkbenchActionConstants.GROUP_FILE));
+
+ // New actions go next
+ MenuManager sub = new MenuManager(Policy.bind("RepositoriesView.newSubmenu"), IWorkbenchActionConstants.GROUP_ADD);
+ sub.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+ manager.add(sub);
+
+ // Misc additions go last
+ manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
+
+ manager.add(refreshAction);
+ sub.add(newAction);
+ }
+ });
+ menuMgr.setRemoveAllWhenShown(true);
+ tree.setMenu(menu);
+ getSite().registerContextMenu(menuMgr, viewer);
+
+ // Create the local tool bar
+ IToolBarManager tbm = getViewSite().getActionBars().getToolBarManager();
+ drillPart.addNavigationActions(tbm);
+ tbm.add(refreshAction);
+ tbm.update(false);
+
+ // Create the open action for double clicks
+ openAction = new OpenRemoteFileAction();
+ viewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent e) {
+ handleDoubleClick(e);
+ }
+ });
+ }
+
+ /*
+ * @see WorkbenchPart#createPartControl
+ */
+ public void createPartControl(Composite parent) {
+ initialize();
+ viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
+ viewer.setContentProvider(new RemoteContentProvider());
+ viewer.setLabelProvider(new WorkbenchLabelProvider());
+ viewer.setInput(roots);
+ drillPart = new DrillDownAdapter(viewer);
+ contributeActions();
+ }
+
+ /**
+ * The mouse has been double-clicked in the tree, perform appropriate
+ * behaviour.
+ */
+ private void handleDoubleClick(DoubleClickEvent e) {
+ // Only act on single selection
+ ISelection selection = e.getSelection();
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection structured = (IStructuredSelection)selection;
+ if (structured.size() == 1) {
+ Object first = structured.getFirstElement();
+ if (first instanceof IRemoteFile) {
+ // It's a file, open it.
+ openAction.selectionChanged(null, selection);
+ openAction.run(null);
+ } else {
+ // Try to expand/contract
+ viewer.setExpandedState(first, !viewer.getExpandedState(first));
+ }
+ }
+ }
+ }
+
+ /**
+ * Initialize the repositories and actions
+ */
+ private void initialize() {
+ roots = new AdaptableList();
+ }
+ /*
+ * @see WorkbenchPart#setFocus
+ */
+ public void setFocus() {
+ viewer.getControl().setFocus();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ResourcePropertiesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ResourcePropertiesPage.java
new file mode 100644
index 000000000..91cbed662
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/ResourcePropertiesPage.java
@@ -0,0 +1,95 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.ui.dialogs.PropertyPage;
+
+/**
+ * A property page which displays the CVS-specific properties for the
+ * selected resource.
+ */
+public class ResourcePropertiesPage extends PropertyPage {
+ // The resource to show properties for
+ IResource resource;
+
+ /*
+ * @see PreferencePage#createContents(Composite)
+ */
+ protected Control createContents(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginHeight = layout.marginWidth = 0;
+ composite.setLayout(layout);
+ composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ try {
+ IResource resource = getSelectedElement();
+ if (resource != null) {
+ CVSTeamProvider provider = (CVSTeamProvider)TeamPlugin.getManager().getProvider(resource.getProject());;
+ if (!provider.isManaged(resource)) {
+ createPair(composite, Policy.bind("ResourcePropertiesPage.status"), Policy.bind("ResourcePropertiesPage.notManaged"));
+ } else {
+ createPair(composite, Policy.bind("ResourcePropertiesPage.status"), provider.hasRemote(resource) ? Policy.bind("ResourcePropertiesPage.versioned") : Policy.bind("ResourcePropertiesPage.notVersioned"));
+ createPair(composite, Policy.bind("ResourcePropertiesPage.state"), provider.isCheckedOut(resource) ? Policy.bind("ResourcePropertiesPage.checkedOut") : Policy.bind("ResourcePropertiesPage.checkedIn"));
+
+ //createPair(composite, Policy.bind("ResourcePropertiesPage.baseRevision"), common != null ? common.getVersionName() : Policy.bind("ResourcePropertiesPage.none"));
+ }
+ }
+ } catch (TeamException e) {
+ createPair(composite, Policy.bind("ResourcePropertiesPage.error"), e.getMessage());
+ }
+ return composite;
+ }
+
+ /**
+ * Creates a key-value property pair in the given parent.
+ *
+ * @param parent the parent for the labels
+ * @param left the string for the left label
+ * @param right the string for the right label
+ */
+ protected void createPair(Composite parent, String left, String right) {
+ Label label = new Label(parent, SWT.NONE);
+ label.setText(left);
+
+ label = new Label(parent, SWT.NONE);
+ label.setText(right);
+ label.setToolTipText(right);
+ label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ }
+
+ /**
+ * Returns the element selected when the properties was run
+ *
+ * @return the selected element
+ */
+ protected IResource getSelectedElement() {
+ // get the resource that is the source of this property page
+ IResource resource = null;
+ IAdaptable element = getElement();
+ if (element instanceof IResource) {
+ resource = (IResource)element;
+ } else {
+ Object adapter = element.getAdapter(IResource.class);
+ if (adapter instanceof IResource) {
+ resource = (IResource)adapter;
+ }
+ }
+ return resource;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/UserValidationDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/UserValidationDialog.java
new file mode 100644
index 000000000..00180c4d3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/UserValidationDialog.java
@@ -0,0 +1,186 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+/**
+ * A dialog for prompting for a username and password
+ */
+public class UserValidationDialog extends Dialog {
+ // widgets
+ protected Text usernameField;
+ protected Text passwordField;
+
+ protected String domain;
+ protected String defaultUsername;
+ protected String password = null;
+
+ // whether or not the username can be changed
+ protected boolean isUsernameMutable = true;
+ protected String username = null;
+ protected String message = null;
+
+ /**
+ * Creates a new UserValidationDialog.
+ *
+ * @param parentShell the parent shell
+ * @param location the location
+ * @param defaultName the default user name
+ * @param message a mesage to display to the user
+ */
+ public UserValidationDialog(Shell parentShell, String location, String defaultName, String message) {
+ super(parentShell);
+ this.defaultUsername = defaultName;
+ this.domain = location;
+ this.message = message;
+ }
+ /**
+ * @see Window#configureShell
+ */
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(Policy.bind("UserValidationDialog.required"));
+ // set F1 help
+ //WorkbenchHelp.setHelp(newShell, new Object[] {ICVSHelpContextIds.USER_VALIDATION_DIALOG});
+ }
+ /**
+ * @see Window#create
+ */
+ public void create() {
+ super.create();
+ // add some default values
+ usernameField.setText(defaultUsername);
+
+ if (isUsernameMutable) {
+ // give focus to username field
+ usernameField.selectAll();
+ usernameField.setFocus();
+ } else {
+ usernameField.setEditable(false);
+ passwordField.setFocus();
+ }
+ }
+ /**
+ * @see Dialog#createDialogArea
+ */
+ protected Control createDialogArea(Composite parent) {
+ Composite main = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 3;
+ main.setLayout(layout);
+ main.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ if (message != null) {
+ Label messageLabel = new Label(main, SWT.WRAP);
+ messageLabel.setText(message);
+ messageLabel.setForeground(messageLabel.getDisplay().getSystemColor(SWT.COLOR_RED));
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.horizontalSpan = 3;
+ messageLabel.setLayoutData(data);
+ }
+
+ Label label = new Label(main, SWT.WRAP);
+ if (isUsernameMutable) {
+ label.setText(Policy.bind("UserValidationDialog.labelUser", domain));
+ } else {
+ label.setText(Policy.bind("UserValidationDialog.labelPassword", new Object[] {defaultUsername, domain}));
+ }
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.horizontalSpan = 3;
+ label.setLayoutData(data);
+
+ createUsernameFields(main);
+ createPasswordFields(main);
+
+ return main;
+ }
+ /**
+ * Creates the three widgets that represent the password entry area.
+ *
+ * @param parent the parent of the widgets
+ */
+ protected void createPasswordFields(Composite parent) {
+ new Label(parent, SWT.NONE).setText(Policy.bind("UserValidationDialog.password"));
+
+ passwordField = new Text(parent, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.ENTRY_FIELD_WIDTH);
+ passwordField.setLayoutData(data);
+ passwordField.setEchoChar('*');
+
+ // spacer
+ new Label(parent, SWT.NONE);
+ }
+ /**
+ * Creates the three widgets that represent the user name entry area.
+ *
+ * @param parent the parent of the widgets
+ */
+ protected void createUsernameFields(Composite parent) {
+ new Label(parent, SWT.NONE).setText(Policy.bind("UserValidationDialog.user"));
+
+ usernameField = new Text(parent, SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.ENTRY_FIELD_WIDTH);
+ usernameField.setLayoutData(data);
+
+ // spacer
+ new Label(parent, SWT.NONE);
+ }
+ /**
+ * Returns the password entered by the user, or null
+ * if the user canceled.
+ *
+ * @return the entered password
+ */
+ public String getPassword() {
+ return password;
+ }
+ /**
+ * Returns the username entered by the user, or null
+ * if the user canceled.
+ *
+ * @return the entered username
+ */
+ public String getUsername() {
+ return username;
+ }
+ /**
+ * Notifies that the ok button of this dialog has been pressed.
+ * <p>
+ * The default implementation of this framework method sets
+ * this dialog's return code to <code>Window.OK</code>
+ * and closes the dialog. Subclasses may override.
+ * </p>
+ */
+ protected void okPressed() {
+ password = passwordField.getText();
+ username = usernameField.getText();
+
+ super.okPressed();
+ }
+ /**
+ * Sets whether or not the username field should be mutable.
+ * This method must be called before create(), otherwise it
+ * will be ignored.
+ *
+ * @param value whether the username is mutable
+ */
+ public void setUsernameMutable(boolean value) {
+ isUsernameMutable = value;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/WorkbenchUserAuthenticator.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/WorkbenchUserAuthenticator.java
new file mode 100644
index 000000000..70cae403c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/WorkbenchUserAuthenticator.java
@@ -0,0 +1,163 @@
+package org.eclipse.team.internal.ccvs.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.ccvs.core.IUserAuthenticator;
+import org.eclipse.team.ccvs.core.IUserInfo;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+
+/**
+ * An authenticator that prompts the user for authentication info,
+ * and stores the results in the Platform's authentication keyring.
+ */
+public class WorkbenchUserAuthenticator implements IUserAuthenticator {
+
+ public static final String INFO_PASSWORD = "org.eclipse.team.ccvs.ui.password";
+ public static final String INFO_USERNAME = "org.eclipse.team.ccvs.ui.username";
+ public static final String AUTH_SCHEME = "";
+ public static final URL FAKE_URL;
+
+ static {
+ URL temp = null;
+ try {
+ temp = new URL("http://org.eclipse.team.ccvs.ui");
+ } catch (MalformedURLException e) {
+ }
+ FAKE_URL = temp;
+ }
+ /**
+ * WorkbenchUserAuthenticator constructor.
+ */
+ public WorkbenchUserAuthenticator() {
+ super();
+ }
+ /**
+ * @see IUserAuthenticator#authenticateUser
+ */
+ public boolean authenticateUser(final ICVSRepositoryLocation location, final IUserInfo userinfo, final boolean retry, final String message) throws CVSException {
+
+ // first check to see if there is a cached username and password
+ if ((!retry) && (retrievePassword(location, userinfo))) {
+ return true;
+ }
+
+ // ask the user for a password
+ final String[] result = new String[2];
+ Display display = Display.getCurrent();
+ if (display != null) {
+ promptForPassword(location, userinfo.getUsername(), message, userinfo.isUsernameMutable(), result);
+ } else {
+ // sync exec in default thread
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ promptForPassword(location, userinfo.getUsername(), message, userinfo.isUsernameMutable(), result);
+ }
+ });
+ }
+
+ if (result[0] == null) {
+ throw new CVSOperationCancelledException(Policy.bind("WorkbenchUserAuthenticator.cancelled"));
+ }
+
+ updateAndCache(location, userinfo, result[0], result[1]);
+ return true;
+ }
+ /**
+ * Asks the user to enter a password. Places the
+ * results in the supplied string[]. result[0] must
+ * contain the username, result[1] must contain the password.
+ * If the user canceled, both values must be zero.
+ *
+ * @param location the location to obtain the password for
+ * @param username the username
+ * @param message a message to display to the user
+ * @param userMutable whether the user can be changed in the dialog
+ * @param result a String array of length two in which to put the result
+ */
+ private void promptForPassword(ICVSRepositoryLocation location, String username, String message, boolean userMutable, String[] result) {
+ Shell shell = new Shell();
+ UserValidationDialog dialog = new UserValidationDialog(shell, location.getLocation(), (username==null)?"":username, message);
+ dialog.setUsernameMutable(userMutable);
+ dialog.open();
+
+ shell.dispose();
+ result[0] = dialog.getUsername();
+ result[1] = dialog.getPassword();
+ }
+ /**
+ * @see IUserAuthenticator#cachePassword
+ */
+ public void cachePassword(ICVSRepositoryLocation location, IUserInfo userinfo, String password) throws CVSException {
+ updateAndCache(location, userinfo, userinfo.getUsername(), password);
+ }
+ /**
+ * @see IUserAuthenticator#retrievePassword
+ */
+ public boolean retrievePassword(ICVSRepositoryLocation location, IUserInfo userinfo) throws CVSException {
+ Map map = Platform.getAuthorizationInfo(FAKE_URL, location.getLocation(), AUTH_SCHEME);
+ if (map != null) {
+ String username = (String) map.get(INFO_USERNAME);
+ String password = (String) map.get(INFO_PASSWORD);
+ if (password != null) {
+ if (userinfo.isUsernameMutable())
+ userinfo.setUsername(username);
+ userinfo.setPassword(password);
+ return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * @see IUserAuthenticator#dispose(IRepositoryLocation)
+ */
+ public void dispose(ICVSRepositoryLocation location) throws CVSException {
+ try {
+ Platform.flushAuthorizationInfo(FAKE_URL, location.getLocation(), AUTH_SCHEME);
+ } catch (CoreException e) {
+ // We should probably wrap the CoreException here!
+ CVSUIPlugin.log(e.getStatus());
+ throw new CVSException(e.getStatus());
+ }
+ }
+ /**
+ * Updates the pasword in the platform keyring.
+ *
+ * @param location the repository location
+ * @param userinfo the user information
+ * @param username the name of the user
+ * @param password the password
+ * @throws CVSException if a CVS error occurs
+ */
+ public void updateAndCache(ICVSRepositoryLocation location, IUserInfo userinfo, String username, String password) throws CVSException {
+ // put the password into the Platform map
+ Map map = Platform.getAuthorizationInfo(FAKE_URL, location.getLocation(), AUTH_SCHEME);
+ if (map == null) {
+ map = new java.util.HashMap(10);
+ }
+ map.put(INFO_USERNAME, username);
+ map.put(INFO_PASSWORD, password);
+ try {
+ Platform.addAuthorizationInfo(FAKE_URL, location.getLocation(), AUTH_SCHEME, map);
+ } catch (CoreException e) {
+ // We should probably wrap the CoreException here!
+ CVSUIPlugin.log(e.getStatus());
+ throw new CVSException(e.getStatus());
+ }
+ if (userinfo.isUsernameMutable()) {
+ userinfo.setUsername(username);
+ }
+ userinfo.setPassword(password);;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddAction.java
new file mode 100644
index 000000000..01a657c3a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddAction.java
@@ -0,0 +1,77 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * AddAction performs a 'cvs add' command on the selected resources. If a
+ * container is selected, its children are recursively added.
+ */
+public class AddAction extends TeamAction {
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("AddAction.adding"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.add(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("AddAction.add"), this.PROGRESS_DIALOG);
+
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ try {
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (((CVSTeamProvider)provider).isManaged(resources[i])) return false;
+ }
+ return true;
+ } catch (TeamException e) {
+ return false;
+ }
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddToWorkspaceAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddToWorkspaceAction.java
new file mode 100644
index 000000000..cdfa7cd89
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/AddToWorkspaceAction.java
@@ -0,0 +1,98 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * Add some remote resources to the workspace. Current implementation:
+ * -Works only for remote folders
+ * -Does not prompt for project name; uses folder name instead
+ */
+public class AddToWorkspaceAction extends TeamAction {
+ /**
+ * Returns the selected remote folders
+ */
+ protected IRemoteFolder[] getSelectedRemoteFolders() {
+ ArrayList resources = null;
+ if (!selection.isEmpty()) {
+ resources = new ArrayList();
+ Iterator elements = ((IStructuredSelection) selection).iterator();
+ while (elements.hasNext()) {
+ Object next = elements.next();
+ if (next instanceof IRemoteFolder) {
+ resources.add(next);
+ continue;
+ }
+ if (next instanceof IAdaptable) {
+ IAdaptable a = (IAdaptable) next;
+ Object adapter = a.getAdapter(IRemoteFolder.class);
+ if (adapter instanceof IRemoteFolder) {
+ resources.add(adapter);
+ continue;
+ }
+ }
+ }
+ }
+ if (resources != null && !resources.isEmpty()) {
+ IRemoteFolder[] result = new IRemoteFolder[resources.size()];
+ resources.toArray(result);
+ return result;
+ }
+ return new IRemoteFolder[0];
+ }
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ IRemoteFolder[] folders = getSelectedRemoteFolders();
+ IProject[] projects = new IProject[folders.length];
+ for (int i = 0; i < folders.length; i++) {
+ String name = folders[i].getName();
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(name);
+ if (project.exists()) {
+ // Make sure the user understands they will overwrite the project.
+ }
+ projects[i] = project;
+ }
+ CVSTeamProvider.checkout(folders, projects, monitor);
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("AddToWorkspaceAction.add"), this.PROGRESS_DIALOG);
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IRemoteFolder[] resources = getSelectedRemoteFolders();
+ if (resources.length == 0) return false;
+ for (int i = 0; i < resources.length; i++) {
+ if (resources[i] instanceof IRemoteRoot) return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAction.java
new file mode 100644
index 000000000..fff2b1d26
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CheckoutAction.java
@@ -0,0 +1,65 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.internal.ccvs.ui.wizards.CheckoutWizard;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+/**
+ * CheckoutAction prompts the user for the location of an existing
+ * project in a CVS repository, and then checks the project out of
+ * the repository, placing it in the workspace.
+ *
+ * This action provides temporary functionality until a full CVS
+ * repository explorer view can be implemented.
+ */
+public class CheckoutAction implements IWorkbenchWindowActionDelegate {
+
+ private IWorkbenchWindow window;
+
+ /*
+ * @see IWorkbenchWindowActionDelegate#dispose
+ */
+ public void dispose() {
+ }
+
+ /**
+ * Convenience method for getting the shell
+ *
+ * @return the shell
+ */
+ protected Shell getShell() {
+ return window.getShell();
+ }
+
+ /*
+ * @see IWorkbenchWindowActionDelegate#init
+ */
+ public void init(IWorkbenchWindow window) {
+ this.window = window;
+ }
+
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ CheckoutWizard wizard = new CheckoutWizard();
+ WizardDialog dialog = new WizardDialog(getShell(), wizard);
+ dialog.open();
+ }
+
+ /*
+ * @see IActionDelegate#selectionChanged
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CommitAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CommitAction.java
new file mode 100644
index 000000000..af696299b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CommitAction.java
@@ -0,0 +1,90 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.ReleaseCommentDialog;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * Action for checking in files to a CVS provider.
+ * Prompts the user for a release comment.
+ */
+public class CommitAction extends TeamAction {
+
+ // The previously remembered comment
+ private static String previousComment = "";
+
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ ReleaseCommentDialog dialog = new ReleaseCommentDialog(getShell());
+ dialog.setComment(previousComment);
+ int result = dialog.open();
+ if (result != ReleaseCommentDialog.OK) return;
+ previousComment = dialog.getComment();
+
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("CommitAction.committing"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
+ provider.setComment(previousComment);
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.checkin(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("CommitAction.commit"), this.PROGRESS_DIALOG);
+
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ try {
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ CVSTeamProvider cvsProvider = (CVSTeamProvider)provider;
+ if (!cvsProvider.isManaged(resources[i])) return false;
+ if (!cvsProvider.isCheckedOut(resources[i])) return false;
+ }
+ return true;
+ } catch (TeamException e) {
+ return false;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/DiffAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/DiffAction.java
new file mode 100644
index 000000000..80de4bd3b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/DiffAction.java
@@ -0,0 +1,80 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * DiffAction performs a 'cvs diff' operation on the selected resources.
+ * The output is presented in the CVS Console. This action will be modified
+ * in the future to use the compare framework.
+ */
+public class DiffAction extends TeamAction {
+ // The previously remembered tag
+ private static String previousTag = "";
+
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.diff(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("DiffAction.diff"), this.PROGRESS_DIALOG);
+
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ try {
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!((CVSTeamProvider)provider).isManaged(resources[i])) return false;
+ }
+ return true;
+ } catch (TeamException e) {
+ return false;
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/OpenRemoteFileAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/OpenRemoteFileAction.java
new file mode 100644
index 000000000..3cf479de6
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/OpenRemoteFileAction.java
@@ -0,0 +1,102 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.RemoteFileEditorInput;
+import org.eclipse.team.ui.actions.TeamAction;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+
+public class OpenRemoteFileAction extends TeamAction {
+ /**
+ * Returns the selected remote files
+ */
+ protected IRemoteFile[] getSelectedRemoteFiles() {
+ ArrayList resources = null;
+ if (!selection.isEmpty()) {
+ resources = new ArrayList();
+ Iterator elements = ((IStructuredSelection) selection).iterator();
+ while (elements.hasNext()) {
+ Object next = elements.next();
+ if (next instanceof IRemoteFile) {
+ resources.add(next);
+ continue;
+ }
+ if (next instanceof IAdaptable) {
+ IAdaptable a = (IAdaptable) next;
+ Object adapter = a.getAdapter(IRemoteFile.class);
+ if (adapter instanceof IRemoteFile) {
+ resources.add(adapter);
+ continue;
+ }
+ }
+ }
+ }
+ if (resources != null && !resources.isEmpty()) {
+ IRemoteFile[] result = new IRemoteFile[resources.size()];
+ resources.toArray(result);
+ return result;
+ }
+ return new IRemoteFile[0];
+ }
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ IWorkbenchPage page = CVSUIPlugin.getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ IRemoteFile[] files = getSelectedRemoteFiles();
+ for (int i = 0; i < files.length; i++) {
+ try {
+ page.openEditor(new RemoteFileEditorInput(files[i]), "org.eclipse.ui.DefaultTextEditor");
+ } catch (PartInitException e) {
+ CVSUIPlugin.log(e.getStatus());
+ }
+ }
+ }
+ }, Policy.bind("OpenRemoteFileAction.open"), this.PROGRESS_DIALOG);
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IRemoteFile[] resources = getSelectedRemoteFiles();
+ if (resources.length == 0) return false;
+ return true;
+ }
+ /** (Non-javadoc)
+ * Method declared on IActionDelegate.
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ this.selection = (IStructuredSelection) selection;
+ //this action can be invoked by double-click, in which case
+ //there is no target action
+ if (action != null) {
+ try {
+ action.setEnabled(isEnabled());
+ } catch (TeamException e) {
+ action.setEnabled(false);
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java
new file mode 100644
index 000000000..d6f9c0883
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java
@@ -0,0 +1,84 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * TagAction tags the selected resources with a version tag specified by the user.
+ */
+public class TagAction extends TeamAction {
+ // The previously remembered tag
+ private static String previousTag = "";
+
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ // Prompt for the tag
+ InputDialog dialog = new InputDialog(getShell(), Policy.bind("TagAction.tagResources"), Policy.bind("TagAction.enterTag"), previousTag, null);
+ if (dialog.open() != InputDialog.OK) return;
+ String tag = dialog.getValue();
+ previousTag = tag;
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.tag(providerResources, IResource.DEPTH_INFINITE, tag, false, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("TagAction.tag"), this.PROGRESS_DIALOG);
+
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ try {
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!((CVSTeamProvider)provider).isManaged(resources[i])) return false;
+ }
+ return true;
+ } catch (TeamException e) {
+ return false;
+ }
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateAction.java
new file mode 100644
index 000000000..e79ca5d40
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateAction.java
@@ -0,0 +1,79 @@
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.actions.TeamAction;
+
+/**
+ * UpdateAction performs a 'cvs update' command on the selected resources.
+ * If conflicts are present (file has been changed both remotely and locally),
+ * the changes will be merged into the local file such that the user must
+ * resolve the conflicts. This action is temporary code; it will be removed
+ * when a functional synchronize view has been implemented.
+ */
+public class UpdateAction extends TeamAction {
+ /*
+ * @see IActionDelegate#run(IAction)
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("UpdateAction.updating"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ CVSTeamProvider provider = (CVSTeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.update(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("UpdateAction.update"), this.PROGRESS_DIALOG);
+
+ }
+ /*
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ try {
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!((CVSTeamProvider)provider).isManaged(resources[i])) return false;
+ }
+ return true;
+ } catch (TeamException e) {
+ return false;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties
new file mode 100644
index 000000000..8cf1055b7
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/messages.properties
@@ -0,0 +1,93 @@
+simpleInternal=Internal error
+
+AddAction.add=Add
+AddAction.adding=Adding...
+
+AddToWorkspaceAction.add=Add to Workspace
+
+AddWizard.title=Add to Workspace
+AddWizard.description=Check out the selected module from the CVS server
+
+CVSDecorator.projectDecoration={0} [{1}]
+CVSDecorator.fileDecorationWithTag={0} [{1}:{2}]
+CVSDecorator.fileDecorationNoTag={0} [{1}]
+
+CVSOperationCancelledException.operationCancelled=Operation Cancelled
+
+CVSPropertiesPage.connectionType=Connection type:
+CVSPropertiesPage.user=User:
+CVSPropertiesPage.password=Password:
+
+CheckoutWizard.title=Check out Module from CVS
+CheckoutWizard.description=Check out a module from a CVS server
+CheckoutWizard.checkout=Check Out
+
+CommitAction.commit=Commit
+CommitAction.committing=Committing...
+
+ConfigurationWizard.title=Import Project into CVS
+ConfigurationWizard.description=Import a project into CVS
+ConfigurationWizard.import=Import
+
+ConfigurationWizardMainPage.connection=Connection type:
+ConfigurationWizardMainPage.userName=User name:
+ConfigurationWizardMainPage.password=Password:
+ConfigurationWizardMainPage.host=Host:
+ConfigurationWizardMainPage.useDefaultPort=Use Default Port
+ConfigurationWizardMainPage.usePort=Use Port:
+ConfigurationWizardMainPage.repositoryPath=Repository path:
+ConfigurationWizardMainPage.module=Module:
+ConfigurationWizardMainPage.projectIsModule=Project Name is Module Name
+ConfigurationWizardMainPage.projectName=Project Name:
+ConfigurationWizardMainPage.checkoutHead=Checkout HEAD
+ConfigurationWizardMainPage.checkoutTag=Checkout Stream or Version:
+ConfigurationWizardMainPage.invalidUserName=Invalid User Name
+ConfigurationWizardMainPage.invalidHostName=Invalid Host Name
+ConfigurationWizardMainPage.invalidPort=Invalid Port
+ConfigurationWizardMainPage.moduleIsProject=Module Name is Project Name
+
+Console.copy=Copy
+Console.selectAll=Select All
+Console.clearOutput=Clear Output
+
+DiffAction.diff=Diff
+
+FixTypeAction.fix=Fix Types
+
+LocationWizard.title=Add a new CVS Repository
+LocationWizard.description=Add a new CVS Repository to the Repositories View
+
+OpenRemoteFileAction.open=Open
+
+ReleaseCommentDialog.title=Commit Comment
+ReleaseCommentDialog.enterComment=Please enter a comment.
+
+RepositoriesView.refresh=Refresh View
+RepositoriesView.new=CVS Repository Location...
+RepositoriesView.newSubmenu=New
+
+ResourcePropertiesPage.status=Status
+ResourcePropertiesPage.notManaged=Not managed by CVS
+ResourcePropertiesPage.versioned=versioned
+ResourcePropertiesPage.notVersioned=not versioned
+ResourcePropertiesPage.state=State
+ResourcePropertiesPage.checkedOut=Checked Out
+ResourcePropertiesPage.checkedIn=Checked In
+#ResourcePropertiesPage.baseRevision=Base Revision
+#ResourcePropertiesPage.none=none
+ResourcePropertiesPage.error=Error
+
+TagAction.tag=Tag
+TagAction.tagResources=Tag Resources
+TagAction.enterTag=Please enter a version tag:
+
+UpdateAction.update=Update
+UpdateAction.updating=Updating...
+
+UserValidationDialog.required=Password Required
+UserValidationDialog.labelUser=Enter a user name and password for the following repository:\n{0}
+UserValidationDialog.labelPassword=Enter a password for {0} in the following repository:\n{1}
+UserValidationDialog.password=Password:
+UserValidationDialog.user=User name:
+
+WorkbenchUserAuthenticator.cancelled=Operation cancelled because login cancelled
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSAdapterFactory.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSAdapterFactory.java
new file mode 100644
index 000000000..468be4a56
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSAdapterFactory.java
@@ -0,0 +1,41 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+public class CVSAdapterFactory implements IAdapterFactory {
+ private Object fileAdapter = new RemoteFileElement();
+ private Object folderAdapter = new RemoteFolderElement();
+ private Object rootAdapter = new RemoteRootElement();
+
+ /** (Non-javadoc)
+ * Method declared on IAdapterFactory.
+ */
+ public Object getAdapter(Object adaptableObject, Class adapterType) {
+ if (IWorkbenchAdapter.class == adapterType) {
+ if (adaptableObject instanceof IRemoteFile) {
+ return fileAdapter;
+ } else if (adaptableObject instanceof IRemoteRoot) {
+ return rootAdapter;
+ } else if (adaptableObject instanceof IRemoteFolder) {
+ return folderAdapter;
+ }
+ return null;
+ }
+ return null;
+ }
+ /** (Non-javadoc)
+ * Method declared on IAdapterFactory.
+ */
+ public Class[] getAdapterList() {
+ return new Class[] {IWorkbenchAdapter.class};
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSModelElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSModelElement.java
new file mode 100644
index 000000000..b1e831e49
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/CVSModelElement.java
@@ -0,0 +1,13 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+public abstract class CVSModelElement implements IWorkbenchAdapter {
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteContentProvider.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteContentProvider.java
new file mode 100644
index 000000000..e7953c2cd
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteContentProvider.java
@@ -0,0 +1,32 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.ccvs.core.IRemoteResource;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+
+/**
+ * Extension to the generic workbench content provider mechanism
+ * to lazily determine whether an element has children. That is,
+ * children for an element aren't fetched until the user clicks
+ * on the tree expansion box.
+ */
+public class RemoteContentProvider extends WorkbenchContentProvider {
+ /* (non-Javadoc)
+ * Method declared on WorkbenchContentProvider.
+ */
+ public boolean hasChildren(Object element) {
+ if (element == null) {
+ return false;
+ }
+ // the + box will always appear, but then disappear
+ // if not needed after you first click on it.
+ if (element instanceof IRemoteResource) {
+ return ((IRemoteResource)element).getType() != IRemoteResource.FILE;
+ }
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFileElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFileElement.java
new file mode 100644
index 000000000..383fdeb03
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFileElement.java
@@ -0,0 +1,42 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.ccvs.core.IRemoteFile;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.ui.PlatformUI;
+
+public class RemoteFileElement extends RemoteResourceElement {
+ /**
+ * Initial implementation: return null;
+ */
+ public Object[] getChildren(Object o) {
+ return null;
+ }
+ /**
+ * Initial implementation: return null.
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ if (!(object instanceof IRemoteFile)) return null;
+ return PlatformUI.getWorkbench().getEditorRegistry().getImageDescriptor(((IRemoteFile)object).getName());
+ }
+ /**
+ * Initial implementation: return the file's name and version
+ */
+ public String getLabel(Object o) {
+ if (!(o instanceof IRemoteFile)) return null;
+ IRemoteFile file = (IRemoteFile)o;
+ try {
+ return file.getName() + " " + file.getRevision(new NullProgressMonitor());
+ } catch (TeamException e) {
+ CVSUIPlugin.log(e.getStatus());
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFolderElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFolderElement.java
new file mode 100644
index 000000000..60cad3c9d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteFolderElement.java
@@ -0,0 +1,35 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.ccvs.core.IRemoteFolder;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+
+public class RemoteFolderElement extends RemoteResourceElement {
+ /**
+ * Initial implementation: return members
+ */
+ public Object[] getChildren(Object o) {
+ if (!(o instanceof IRemoteFolder)) return null;
+ try {
+ return ((IRemoteFolder)o).getMembers(new NullProgressMonitor());
+ } catch (TeamException e) {
+ return null;
+ }
+ }
+ /**
+ * Initial implementation: return null.
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ if (!(object instanceof IRemoteFolder)) return null;
+ return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER);
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteResourceElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteResourceElement.java
new file mode 100644
index 000000000..f44a8a282
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteResourceElement.java
@@ -0,0 +1,25 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.team.ccvs.core.IRemoteResource;
+
+public abstract class RemoteResourceElement extends CVSModelElement {
+ /**
+ * Initial implementation: return the resource's name
+ */
+ public String getLabel(Object o) {
+ if (!(o instanceof IRemoteResource)) return null;
+ return ((IRemoteResource)o).getName();
+ }
+ /**
+ * Return null.
+ */
+ public Object getParent(Object o) {
+ if (!(o instanceof IRemoteResource)) return null;
+ return ((IRemoteResource)o).getParent();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteRootElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteRootElement.java
new file mode 100644
index 000000000..6b7246bc1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/model/RemoteRootElement.java
@@ -0,0 +1,44 @@
+package org.eclipse.team.internal.ccvs.ui.model;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.ccvs.core.IRemoteRoot;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
+
+public class RemoteRootElement extends RemoteFolderElement {
+ /**
+ * Initial implementation: return null.
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ if (!(object instanceof IRemoteRoot)) return null;
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_REPOSITORY);
+ }
+ /**
+ * Initial implementation. Doesn't handle ports.
+ */
+ public String getLabel(Object o) {
+ if (!(o instanceof IRemoteRoot)) return null;
+ IRemoteRoot root = (IRemoteRoot)o;
+ StringBuffer result = new StringBuffer();
+ result.append(":");
+ result.append(root.getConnectionMethod());
+ result.append(":");
+ result.append(root.getUser());
+ result.append("@");
+ result.append(root.getHost());
+ result.append(":");
+ result.append(root.getRepositoryPath());
+ return result.toString();
+ }
+ /**
+ * Return null.
+ */
+ public Object getParent(Object o) {
+ return null;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/AddWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/AddWizard.java
new file mode 100644
index 000000000..051c54bc2
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/AddWizard.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * This wizard helps the user to add selected projects
+ * to the workspace.
+ */
+public class AddWizard extends ConnectionWizard {
+ /**
+ * AddWizard constructor
+ */
+ public AddWizard() {
+ IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
+ IDialogSettings section = workbenchSettings.getSection("CVSWizard");//$NON-NLS-1$
+ if (section == null) {
+ section = workbenchSettings.addNewSection("CVSWizard");//$NON-NLS-1$
+ }
+ setDialogSettings(section);
+ }
+ protected String getMainPageDescription() {
+ return Policy.bind("AddWizard.description");
+ }
+ protected String getMainPageTitle() {
+ return Policy.bind("AddWizard.title");
+ }
+ protected int getStyle() {
+ return ConfigurationWizardMainPage.PROJECT_NAME |
+ ConfigurationWizardMainPage.TAG;
+ }
+ /*
+ * @see IWizard#performFinish
+ */
+ public boolean performFinish() {
+ getMainPage().finish(new NullProgressMonitor());
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizard.java
new file mode 100644
index 000000000..ecbd5d28f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizard.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.team.internal.ccvs.ui.*;
+
+public abstract class CVSWizard extends Wizard {
+ /**
+ * Convenience method for running an operation with progress and
+ * error feedback.
+ *
+ * @param runnable the runnable to run
+ * @param problemMessage an optional message to display in case of errors
+ */
+ protected void run(IRunnableWithProgress runnable, String problemMessage) {
+ IStatus errors = null;
+ boolean internalError = false;
+ try {
+ new ProgressMonitorDialog(getShell()).run(false, false, runnable);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ if (t instanceof CoreException) {
+ errors = ((CoreException)t).getStatus();
+ } else {
+ errors = new Status(IStatus.ERROR, CVSUIPlugin.ID, 1, problemMessage, t);
+ internalError = true;
+ }
+ } catch (InterruptedException e) {
+ errors = null;
+ }
+ if (errors != null) {
+ String msg = internalError ? Policy.bind("simpleInternal") : problemMessage;
+ ErrorDialog.openError(getShell(), msg, null, errors);
+ CVSUIPlugin.log(errors);
+ }
+ }
+
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizardPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizardPage.java
new file mode 100644
index 000000000..bbcef914f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CVSWizardPage.java
@@ -0,0 +1,140 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.team.internal.ccvs.ui.*;
+
+/**
+ * Common superclass for CVS wizard pages. Provides convenience methods
+ * for widget creation.
+ */
+public abstract class CVSWizardPage extends WizardPage implements Listener {
+ /**
+ * CVSWizardPage constructor comment.
+ * @param pageName the name of the page
+ */
+ public CVSWizardPage(String pageName) {
+ super(pageName);
+ }
+ /**
+ * CVSWizardPage constructor comment.
+ * @param pageName the name of the page
+ * @param title the title of the page
+ * @param titleImage the image for the page
+ */
+ public CVSWizardPage(String pageName, String title, org.eclipse.jface.resource.ImageDescriptor titleImage) {
+ super(pageName, title, titleImage);
+ }
+ /**
+ * Creates a new checkbox instance and sets the default layout data.
+ *
+ * @param group the composite in which to create the checkbox
+ * @param label the string to set into the checkbox
+ * @return the new checkbox
+ */
+ protected Button createCheckBox(Composite group, String label) {
+ Button button = new Button(group, SWT.CHECK | SWT.LEFT);
+ button.setText(label);
+ button.addListener(SWT.Selection, this);
+ GridData data = new GridData();
+ data.horizontalSpan = 2;
+ button.setLayoutData(data);
+ return button;
+ }
+ /**
+ * Utility method that creates a combo box
+ *
+ * @param parent the parent for the new label
+ * @return the new widget
+ */
+ protected Combo createCombo(Composite parent) {
+ Combo combo = new Combo(parent, SWT.READ_ONLY);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ combo.setLayoutData(data);
+ combo.addListener(SWT.Modify, this);
+ return combo;
+ }
+ /**
+ * Creates composite control and sets the default layout data.
+ *
+ * @param parent the parent of the new composite
+ * @param numColumns the number of columns for the new composite
+ * @return the newly-created coposite
+ */
+ protected Composite createComposite(Composite parent, int numColumns) {
+ Composite composite = new Composite(parent, SWT.NULL);
+
+ // GridLayout
+ GridLayout layout = new GridLayout();
+ layout.numColumns = numColumns;
+ composite.setLayout(layout);
+
+ // GridData
+ GridData data = new GridData();
+ data.verticalAlignment = GridData.FILL;
+ data.horizontalAlignment = GridData.FILL;
+ composite.setLayoutData(data);
+ return composite;
+ }
+ /**
+ * Utility method that creates a label instance
+ * and sets the default layout data.
+ *
+ * @param parent the parent for the new label
+ * @param text the text for the new label
+ * @return the new label
+ */
+ protected Label createLabel(Composite parent, String text) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText(text);
+ GridData data = new GridData();
+ data.horizontalSpan = 1;
+ data.horizontalAlignment = GridData.FILL;
+ label.setLayoutData(data);
+ return label;
+ }
+ /**
+ * Create a text field specific for this application
+ *
+ * @param parent the parent of the new text field
+ * @return the new text field
+ */
+ protected Text createTextField(Composite parent) {
+ Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.verticalAlignment = GridData.CENTER;
+ data.grabExcessVerticalSpace = false;
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ text.setLayoutData(data);
+ text.addListener(SWT.Modify, this);
+ return text;
+ }
+ /**
+ * @see Listener#handleEvent
+ */
+ public void handleEvent (Event event) {
+ updateWidgetEnablements();
+ }
+ /**
+ * Updates widget enablements and sets error message if appropriate.
+ * Subclasses may override. Default behaviour is to do nothing.
+ */
+ protected void updateWidgetEnablements() {
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CheckoutWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CheckoutWizard.java
new file mode 100644
index 000000000..49a230e2b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/CheckoutWizard.java
@@ -0,0 +1,83 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * This wizard helps the user to check out an existing project from
+ * an existing CVS repository location, and places it in the workspace
+ * with the provider set.
+ */
+public class CheckoutWizard extends ConnectionWizard {
+
+ /**
+ * CheckoutWizard constructor
+ */
+ public CheckoutWizard() {
+ IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
+ IDialogSettings section = workbenchSettings.getSection("CVSWizard");//$NON-NLS-1$
+ if (section == null) {
+ section = workbenchSettings.addNewSection("CVSWizard");//$NON-NLS-1$
+ }
+ setDialogSettings(section);
+ }
+ protected String getMainPageDescription() {
+ return Policy.bind("CheckoutWizard.description");
+ }
+ protected String getMainPageTitle() {
+ return Policy.bind("CheckoutWizard.title");
+ }
+ protected int getStyle() {
+ return ConfigurationWizardMainPage.CONNECTION_METHOD |
+ ConfigurationWizardMainPage.USER |
+ ConfigurationWizardMainPage.PASSWORD |
+ ConfigurationWizardMainPage.PORT |
+ ConfigurationWizardMainPage.HOST |
+ ConfigurationWizardMainPage.REPOSITORY_PATH |
+ ConfigurationWizardMainPage.MODULE_TEXT |
+ ConfigurationWizardMainPage.PROJECT_NAME |
+ ConfigurationWizardMainPage.TAG;
+ }
+ /*
+ * @see IWizard#performFinish
+ */
+ public boolean performFinish() {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ getMainPage().finish(monitor);
+ Properties properties = getProperties();
+ // Prepare the location
+ String projectName = properties.getProperty("project");
+ if (projectName == null) {
+ projectName = properties.getProperty("module");
+ }
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ if (project.exists()) {
+ // Make sure the user understands they will overwrite the project.
+ }
+ CVSTeamProvider.checkout(project, properties, monitor);
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("CheckoutWizard.checkout"));
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizard.java
new file mode 100644
index 000000000..0d59b0449
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizard.java
@@ -0,0 +1,84 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Properties;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.ui.IConfigurationWizard;
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * This wizard helps the user to import a new project in their workspace
+ * into a CVS repository for the first time.
+ */
+public class ConfigurationWizard extends ConnectionWizard implements IConfigurationWizard {
+ // The project to configure
+ private IProject project;
+
+ /**
+ * ConfigurationWizard constructor
+ */
+ public ConfigurationWizard() {
+ IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
+ IDialogSettings section = workbenchSettings.getSection("CVSWizard");//$NON-NLS-1$
+ if (section == null) {
+ section = workbenchSettings.addNewSection("CVSWizard");//$NON-NLS-1$
+ }
+ setDialogSettings(section);
+ }
+ protected String getMainPageDescription() {
+ return Policy.bind("ConfigurationWizard.description");
+ }
+ protected String getMainPageTitle() {
+ return Policy.bind("ConfigurationWizard.title");
+ }
+ protected int getStyle() {
+ return ConfigurationWizardMainPage.CONNECTION_METHOD |
+ ConfigurationWizardMainPage.USER |
+ ConfigurationWizardMainPage.PASSWORD |
+ ConfigurationWizardMainPage.PORT |
+ ConfigurationWizardMainPage.HOST |
+ ConfigurationWizardMainPage.REPOSITORY_PATH |
+ ConfigurationWizardMainPage.MODULE_RADIO;
+ }
+ /*
+ * @see IWizard#performFinish
+ */
+ public boolean performFinish() {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ getMainPage().finish(monitor);
+ // Get the result of the wizard page
+ Properties properties = getProperties();
+
+ if (properties.getProperty("module") == null) {
+ properties.setProperty("module", project.getName());
+ }
+ CVSTeamProvider.importAndCheckoutProject(project, properties, monitor);
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("ConfigurationWizard.import"));
+ return true;
+ }
+ /*
+ * @see IConfigurationWizard#init(IWorkbench, IProject)
+ */
+ public void init(IWorkbench workbench, IProject project) {
+ this.project = project;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizardMainPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizardMainPage.java
new file mode 100644
index 000000000..0741d1624
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConfigurationWizardMainPage.java
@@ -0,0 +1,615 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.team.ccvs.core.CVSTeamProvider;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * Wizard page for configuring a project with a CVS repository location.
+ *
+ * This is a multi-purpose wizard page. Its contents are determined by a
+ * bitmask of capabilities set with setStyle().
+ */
+public class ConfigurationWizardMainPage extends CVSWizardPage {
+ public static final int CONNECTION_METHOD = 1 << 0;
+ public static final int USER = 1 << 1;
+ public static final int PASSWORD = 1 << 2;
+ public static final int HOST = 1 << 3;
+ public static final int PORT = 1 << 4;
+ public static final int REPOSITORY_PATH = 1 << 5;
+ public static final int MODULE_TEXT = 1 << 6;
+ public static final int MODULE_RADIO = 1 << 7;
+ public static final int PROJECT_NAME = 1 << 8;
+ public static final int TAG = 1 << 9;
+ public static final int PROJECT_LOCATION = 1 << 10;
+
+ // The mode for the page
+ int style;
+
+ // Widgets
+
+ // Connection Method
+ private Combo connectionMethodCombo;
+ // User
+ private Combo userCombo;
+ // Password
+ private Text passwordText;
+ // Port
+ private Text portText;
+ private Button useDefaultPort;
+ private Button useCustomPort;
+ // Host
+ private Combo hostCombo;
+ // Repository Path
+ private Combo repositoryPathCombo;
+ // Module combo
+ private Combo moduleCombo;
+ // Module radio
+ private Button moduleIsProject;
+ private Button moduleIsCustom;
+ private Text moduleText;
+ // Project name
+ private Button projectIsModule;
+ private Button projectIsCustom;
+ private Text projectText;
+ // Tag
+ private Button checkoutHead;
+ private Button checkoutTag;
+ private Combo tagCombo;
+
+ private static final int COMBO_HISTORY_LENGTH = 5;
+
+ private Properties properties = new Properties();
+
+ // Dialog store id constants
+ private static final String STORE_USERNAME_ID =
+ "ConfigurationWizardMainPage.STORE_USERNAME_ID";//$NON-NLS-1$
+ private static final String STORE_HOSTNAME_ID =
+ "ConfigurationWizardMainPage.STORE_HOSTNAME_ID";//$NON-NLS-1$
+ private static final String STORE_PATH_ID =
+ "ConfigurationWizardMainPage.STORE_PATH_ID";//$NON-NLS-1$
+ private static final String STORE_MODULE_ID =
+ "ConfigurationWizardMainPage.STORE_MODULE_ID";//$NON-NLS-1$
+ private static final String STORE_TAG_ID =
+ "ConfigurationWizardMainPage.STORE_TAG_ID";//$NON-NLS-1$
+
+ /**
+ * ConfigurationWizardMainPage constructor.
+ *
+ * @param pageName the name of the page
+ * @param title the title of the page
+ * @param titleImage the image for the page
+ */
+ public ConfigurationWizardMainPage(String pageName, String title, ImageDescriptor titleImage) {
+ super(pageName, title, titleImage);
+ }
+ /**
+ * Adds an entry to a history, while taking care of duplicate history items
+ * and excessively long histories. The assumption is made that all histories
+ * should be of length <code>ConfigurationWizardMainPage.COMBO_HISTORY_LENGTH</code>.
+ *
+ * @param history the current history
+ * @param newEntry the entry to add to the history
+ * @return the history with the new entry appended
+ */
+ private String[] addToHistory(String[] history, String newEntry) {
+ ArrayList l = new ArrayList(Arrays.asList(history));
+ addToHistory(l, newEntry);
+ String[] r = new String[l.size()];
+ l.toArray(r);
+ return r;
+ }
+ /**
+ * Adds an entry to a history, while taking care of duplicate history items
+ * and excessively long histories. The assumption is made that all histories
+ * should be of length <code>ConfigurationWizardMainPage.COMBO_HISTORY_LENGTH</code>.
+ *
+ * @param history the current history
+ * @param newEntry the entry to add to the history
+ */
+ private void addToHistory(List history, String newEntry) {
+ history.remove(newEntry);
+ history.add(0,newEntry);
+
+ // since only one new item was added, we can be over the limit
+ // by at most one item
+ if (history.size() > COMBO_HISTORY_LENGTH)
+ history.remove(COMBO_HISTORY_LENGTH);
+ }
+ /**
+ * Creates the UI part of the page.
+ *
+ * @param parent the parent of the created widgets
+ */
+ public void createControl(Composite parent) {
+ Composite composite = createComposite(parent, 2);
+ // set F1 help
+ // WorkbenchHelp.setHelp(composite, new DialogPageContextComputer (this, ITeamHelpContextIds.REPO_CONNECTION_MAIN_PAGE));
+
+ if ((style & CONNECTION_METHOD) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.connection"));
+ connectionMethodCombo = createCombo(composite);
+ }
+ if ((style & USER) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.userName"));
+ userCombo = createEditableCombo(composite);
+ }
+ if ((style & PASSWORD) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.password"));
+ passwordText = createTextField(composite);
+ passwordText.setEchoChar('*');
+ }
+ if ((style & HOST) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.host"));
+ hostCombo = createEditableCombo(composite);
+ }
+ if ((style & PORT) != 0) {
+ useDefaultPort = createRadioButton(composite, Policy.bind("ConfigurationWizardMainPage.useDefaultPort"), 2);
+ useCustomPort = createRadioButton(composite, Policy.bind("ConfigurationWizardMainPage.usePort"), 1);
+ portText = createTextField(composite);
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.repositoryPath"));
+ repositoryPathCombo = createEditableCombo(composite);
+ }
+ if ((style & MODULE_TEXT) != 0) {
+ createLabel(composite, Policy.bind("ConfigurationWizardMainPage.module"));
+ moduleCombo = createEditableCombo(composite);
+ } else if ((style & MODULE_RADIO) != 0) {
+ Composite radioComposite = createRadioComposite(composite);
+ moduleIsProject = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.moduleIsProject"), 2);
+ moduleIsCustom = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.module"), 1);
+ moduleText = createTextField(radioComposite);
+ }
+ if ((style & PROJECT_NAME) != 0) {
+ Composite radioComposite = createRadioComposite(composite);
+ projectIsModule = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.projectIsModule"), 2);
+ projectIsCustom = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.projectName"), 1);
+ projectText = createTextField(radioComposite);
+ }
+ if ((style & TAG) != 0) {
+ createLabel(composite, "");
+ createLabel(composite, "");
+ Composite radioComposite = createRadioComposite(composite);
+ checkoutHead = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.checkoutHead"), 2);
+ checkoutTag = createRadioButton(radioComposite, Policy.bind("ConfigurationWizardMainPage.checkoutTag"), 1);
+ tagCombo = createEditableCombo(radioComposite);
+ }
+
+ initializeValues();
+ updateWidgetEnablements();
+ if (userCombo != null) {
+ userCombo.setFocus();
+ }
+
+ setControl(composite);
+ }
+ /**
+ * Utility method to create an editable combo box
+ *
+ * @param parent the parent of the combo box
+ * @return the created combo
+ */
+ protected Combo createEditableCombo(Composite parent) {
+ Combo combo = new Combo(parent, SWT.NULL);
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);
+ data.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH;
+ combo.setLayoutData(data);
+ combo.addListener(SWT.Modify, this);
+ return combo;
+ }
+ /**
+ * Utility method to create a radio button
+ *
+ * @param parent the parent of the radio button
+ * @param label the label of the radio button
+ * @param span the number of columns to span
+ * @return the created radio button
+ */
+ protected Button createRadioButton(Composite parent, String label, int span) {
+ Button button = new Button(parent, SWT.RADIO);
+ button.setText(label);
+ button.addListener(SWT.Selection, this);
+ GridData data = new GridData();
+ data.horizontalSpan = span;
+ button.setLayoutData(data);
+ return button;
+ }
+ /**
+ * Utility method to create a composite for radio buttons
+ *
+ * @param composite the parent
+ * @return the created composite
+ */
+ protected Composite createRadioComposite(Composite composite) {
+ Composite comboComposite = new Composite(composite, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ comboComposite.setLayout(layout);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.horizontalSpan = 2;
+ comboComposite.setLayoutData(data);
+ return comboComposite;
+ }
+ /**
+ * @see CVSWizardPage#finish
+ */
+ public boolean finish(IProgressMonitor monitor) {
+ // Set the result to be the current values
+ Properties result = new Properties();
+ if ((style & CONNECTION_METHOD) != 0) {
+ result.setProperty("connection", connectionMethodCombo.getText());
+ }
+ if ((style & USER) != 0) {
+ result.setProperty("user", userCombo.getText());
+ }
+ if ((style & PASSWORD) != 0) {
+ result.setProperty("password", passwordText.getText());
+ }
+ if ((style & HOST) != 0) {
+ result.setProperty("host", hostCombo.getText());
+ }
+ if ((style & PORT) != 0) {
+ if (useCustomPort.getSelection()) {
+ result.setProperty("port", portText.getText());
+ }
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ result.setProperty("root", repositoryPathCombo.getText());
+ }
+ if ((style & TAG) != 0) {
+ if (checkoutTag.getSelection()) {
+ result.setProperty("tag", tagCombo.getText());
+ }
+ }
+ if ((style & PROJECT_NAME) != 0) {
+ if (projectIsCustom.getSelection()) {
+ result.setProperty("project", projectText.getText());
+ }
+ }
+ if ((style & MODULE_RADIO) != 0) {
+ if (moduleIsCustom.getSelection()) {
+ result.setProperty("module", moduleText.getText());
+ }
+ } else if ((style & MODULE_TEXT) != 0) {
+ result.setProperty("module", moduleCombo.getText());
+ }
+ this.properties = result;
+
+ saveWidgetValues();
+
+ return true;
+ }
+ /**
+ * Returns the properties for the repository connection
+ *
+ * @return the properties or null
+ */
+ public Properties getProperties() {
+ return properties;
+ }
+ /**
+ * Initializes states of the controls.
+ */
+ private void initializeValues() {
+ // Set remembered values
+ IDialogSettings settings = getDialogSettings();
+ if (settings != null) {
+ if ((style & HOST) != 0) {
+ String[] hostNames = settings.getArray(STORE_HOSTNAME_ID);
+ if (hostNames != null) {
+ for (int i = 0; i < hostNames.length; i++) {
+ hostCombo.add(hostNames[i]);
+ }
+ }
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ String[] paths = settings.getArray(STORE_PATH_ID);
+ if (paths != null) {
+ for (int i = 0; i < paths.length; i++) {
+ repositoryPathCombo.add(paths[i]);
+ }
+ }
+ }
+ if ((style & USER) != 0) {
+ String[] userNames = settings.getArray(STORE_USERNAME_ID);
+ if (userNames != null) {
+ for (int i = 0; i < userNames.length; i++) {
+ userCombo.add(userNames[i]);
+ }
+ }
+ }
+ if ((style & MODULE_TEXT) != 0) {
+ String[] moduleNames = settings.getArray(STORE_MODULE_ID);
+ if (moduleNames != null) {
+ for (int i = 0; i < moduleNames.length; i++) {
+ moduleCombo.add(moduleNames[i]);
+ }
+ }
+ }
+ if ((style & TAG) != 0) {
+ String[] tags = settings.getArray(STORE_TAG_ID);
+ if (tags != null) {
+ for (int i = 0; i < tags.length; i++) {
+ tagCombo.add(tags[i]);
+ }
+ }
+ }
+ }
+
+ // Initialize other values and widget states
+ if ((style & CONNECTION_METHOD) != 0) {
+ String[] methods = CVSTeamProvider.getConnectionMethods();
+ for (int i = 0; i < methods.length; i++) {
+ connectionMethodCombo.add(methods[i]);
+ }
+ String method = (String)properties.getProperty("method");
+ if (method == null) {
+ connectionMethodCombo.select(0);
+ } else {
+ connectionMethodCombo.select(connectionMethodCombo.indexOf(method));
+ }
+ }
+ if ((style & USER) != 0) {
+ String user = (String)properties.getProperty("user");
+ if (user != null) {
+ userCombo.setText(user);
+ }
+ }
+ if ((style & PASSWORD) != 0) {
+ String password = (String)properties.getProperty("password");
+ if (password != null) {
+ passwordText.setText(password);
+ }
+ }
+ if ((style & HOST) != 0) {
+ String host = (String)properties.getProperty("host");
+ if (host != null) {
+ hostCombo.setText(host);
+ }
+ }
+ if ((style & PORT) != 0) {
+ String port = (String)properties.getProperty("port");
+ if (port == null) {
+ useDefaultPort.setSelection(true);
+ } else {
+ useCustomPort.setSelection(true);
+ portText.setText(port);
+ }
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ String repositoryPath = (String)properties.getProperty("root");
+ if (repositoryPath != null) {
+ repositoryPathCombo.setText(repositoryPath);
+ }
+ }
+ if ((style & MODULE_RADIO) != 0) {
+ String module = (String)properties.getProperty("module");
+ if (module == null) {
+ moduleIsProject.setSelection(true);
+ } else {
+ moduleIsCustom.setSelection(true);
+ moduleText.setText(module);
+ }
+ } else if ((style & MODULE_TEXT) != 0) {
+ String module = (String)properties.getProperty("module");
+ if (module != null) {
+ moduleCombo.setText(module);
+ }
+ }
+ if ((style & PROJECT_NAME) != 0) {
+ String project = (String)properties.getProperty("project");
+ if (project == null) {
+ projectIsModule.setSelection(true);
+ } else {
+ projectIsCustom.setSelection(true);
+ projectText.setText(project);
+ }
+ }
+ if ((style & TAG) != 0) {
+ String tag = (String)properties.getProperty("tag");
+ if (tag == null) {
+ checkoutHead.setSelection(true);
+ } else {
+ checkoutTag.setSelection(true);
+ tagCombo.setText(tag);
+ }
+ }
+}
+ /**
+ * Saves the widget values
+ */
+ private void saveWidgetValues() {
+ // Update history
+ IDialogSettings settings = getDialogSettings();
+ if (settings != null) {
+ if ((style & USER) != 0) {
+ String[] userNames = settings.getArray(STORE_USERNAME_ID);
+ if (userNames == null) userNames = new String[0];
+ userNames = addToHistory(userNames, userCombo.getText());
+ settings.put(STORE_USERNAME_ID, userNames);
+ }
+ if ((style & HOST) != 0) {
+ String[] hostNames = settings.getArray(STORE_HOSTNAME_ID);
+ if (hostNames == null) hostNames = new String[0];
+ hostNames = addToHistory(hostNames, hostCombo.getText());
+ settings.put(STORE_HOSTNAME_ID, hostNames);
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ String[] paths = settings.getArray(STORE_PATH_ID);
+ if (paths == null) paths = new String[0];
+ paths = addToHistory(paths, repositoryPathCombo.getText());
+ settings.put(STORE_PATH_ID, paths);
+ }
+ if ((style & MODULE_TEXT) != 0) {
+ String[] modules = settings.getArray(STORE_MODULE_ID);
+ if (modules == null) modules = new String[0];
+ modules = addToHistory(modules, moduleCombo.getText());
+ settings.put(STORE_MODULE_ID, modules);
+ }
+ if ((style & TAG) != 0) {
+ String tag = tagCombo.getText();
+ if (!tag.equals("")) {
+ String[] tags = settings.getArray(STORE_TAG_ID);
+ if (tags == null) tags = new String[0];
+ tags = addToHistory(tags, tag);
+ settings.put(STORE_TAG_ID, tags);
+ }
+ }
+ }
+ }
+ /**
+ * Set the style for the wizard page
+ *
+ * @param style the style for the page
+ */
+ public void setStyle(int style) {
+ this.style = style;
+ }
+ /**
+ * Sets the properties for the repository connection
+ *
+ * @param properties the properties or null
+ */
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+ /**
+ * Updates widget enablements and sets error message if appropriate.
+ */
+ protected void updateWidgetEnablements() {
+ if ((style & PORT) != 0) {
+ if (useDefaultPort.getSelection()) {
+ portText.setEnabled(false);
+ } else {
+ portText.setEnabled(true);
+ }
+ }
+ if ((style & PROJECT_NAME) != 0) {
+ if (projectIsModule.getSelection()) {
+ projectText.setEnabled(false);
+ } else {
+ projectText.setEnabled(true);
+ }
+ }
+ if ((style & MODULE_RADIO) != 0) {
+ if (moduleIsProject.getSelection()) {
+ moduleText.setEnabled(false);
+ } else {
+ moduleText.setEnabled(true);
+ }
+ }
+ if ((style & TAG) != 0) {
+ if (checkoutHead.getSelection()) {
+ tagCombo.setEnabled(false);
+ } else {
+ tagCombo.setEnabled(true);
+ }
+ }
+ validateFields();
+ }
+ /**
+ * Validates the contents of the editable fields and set page completion
+ * and error messages appropriately.
+ */
+ private void validateFields() {
+ if ((style & USER) != 0) {
+ String user = userCombo.getText();
+ if (user.length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ if ((user.indexOf('@') != -1) || (user.indexOf(':') != -1)) {
+ setErrorMessage(Policy.bind("ConfigurationWizardMainPage.invalidUserName"));
+ setPageComplete(false);
+ return;
+ }
+ }
+ if ((style & HOST) != 0) {
+ String host = hostCombo.getText();
+ if (host.length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ if (host.indexOf(':') != -1) {
+ setErrorMessage(Policy.bind("ConfigurationWizardMainPage.invalidHostName"));
+ setPageComplete(false);
+ return;
+ }
+ }
+ if ((style & PORT) != 0) {
+ if (portText.isEnabled()) {
+ if (portText.getText().length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ try {
+ Integer.parseInt(portText.getText());
+ } catch (NumberFormatException e) {
+ setErrorMessage(Policy.bind("ConfigurationWizardMainPage.invalidPort"));
+ setPageComplete(false);
+ return;
+ }
+ }
+ }
+ if ((style & REPOSITORY_PATH) != 0) {
+ if (repositoryPathCombo.getText().length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ }
+ if ((style & MODULE_RADIO) != 0) {
+ if (moduleIsCustom.getSelection()) {
+ if (moduleText.getText().length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ }
+ }
+ if ((style & MODULE_TEXT) != 0) {
+ if (moduleCombo.getText().length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ }
+ if ((style & PROJECT_NAME) != 0) {
+ if (projectIsCustom.getSelection()) {
+ if (projectText.getText().length() == 0) {
+ setErrorMessage(null);
+ setPageComplete(false);
+ return;
+ }
+ }
+ }
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConnectionWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConnectionWizard.java
new file mode 100644
index 000000000..7e07d0105
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/ConnectionWizard.java
@@ -0,0 +1,47 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.Properties;
+import org.eclipse.team.internal.ccvs.ui.wizards.*;
+import org.eclipse.team.internal.ccvs.ui.*;
+
+/**
+ * Abstract wizard which displays a configuration wizard main page
+ * in some configuration, and performs some action on finish.
+ */
+public abstract class ConnectionWizard extends CVSWizard {
+ // The main page.
+ private ConfigurationWizardMainPage mainPage;
+
+ private Properties properties;
+
+ /**
+ * Creates the wizard pages
+ */
+ public void addPages() {
+ mainPage = new ConfigurationWizardMainPage("repositoryPage1", getMainPageTitle(), null);
+ if (properties != null) {
+ mainPage.setProperties(properties);
+ }
+ mainPage.setDescription(getMainPageDescription());
+ mainPage.setStyle(getStyle());
+ addPage(mainPage);
+ }
+ protected ConfigurationWizardMainPage getMainPage() {
+ return mainPage;
+ }
+ protected abstract String getMainPageDescription();
+ protected abstract String getMainPageTitle();
+ protected abstract int getStyle();
+ public Properties getProperties() {
+ return mainPage.getProperties();
+ }
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/LocationWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/LocationWizard.java
new file mode 100644
index 000000000..f387409a0
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/LocationWizard.java
@@ -0,0 +1,51 @@
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * This wizard helps the user to add a new repository location
+ * to the repositories view
+ */
+public class LocationWizard extends ConnectionWizard {
+ /**
+ * LocationWizard constructor
+ */
+ public LocationWizard() {
+ IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
+ IDialogSettings section = workbenchSettings.getSection("CVSWizard");//$NON-NLS-1$
+ if (section == null) {
+ section = workbenchSettings.addNewSection("CVSWizard");//$NON-NLS-1$
+ }
+ setDialogSettings(section);
+ }
+ protected String getMainPageDescription() {
+ return Policy.bind("LocationWizard.description");
+ }
+ protected String getMainPageTitle() {
+ return Policy.bind("LocationWizard.title");
+ }
+ protected int getStyle() {
+ return ConfigurationWizardMainPage.CONNECTION_METHOD |
+ ConfigurationWizardMainPage.USER |
+ ConfigurationWizardMainPage.PASSWORD |
+ ConfigurationWizardMainPage.PORT |
+ ConfigurationWizardMainPage.HOST |
+ ConfigurationWizardMainPage.REPOSITORY_PATH;
+ }
+ /*
+ * @see IWizard#performFinish
+ */
+ public boolean performFinish() {
+ getMainPage().finish(new NullProgressMonitor());
+ return true;
+ }
+}
+
diff --git a/bundles/org.eclipse.team.ui/.classpath b/bundles/org.eclipse.team.ui/.classpath
new file mode 100644
index 000000000..6c1e47270
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/.classpath
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="/org.eclipse.team.core"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.compare/compare.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.compare/comparesrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resources.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.resources/resourcessrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.ui/workbench.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.ui/workbench.jar"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.swt/swt.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.swt/swtsrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtime.jar" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.core.runtime/runtimesrc.zip"/>
+ <classpathentry kind="var"
+ path="ECLIPSE_HOME/plugins/org.eclipse.jdt.ui/jdt.jar"
+ rootpath="" sourcepath="ECLIPSE_HOME/plugins/org.eclipse.jdt.ui/jdtsrc.zip"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.team.ui/.cvsignore b/bundles/org.eclipse.team.ui/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/.vcm_meta b/bundles/org.eclipse.team.ui/.vcm_meta
new file mode 100644
index 000000000..8bdbfea83
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/.vcm_meta
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-description>
+ <nature id="org.eclipse.jdt.core.javanature"/>
+ <reference project-name="org.eclipse.team.core"/>
+ <builder name="org.eclipse.jdt.core.javabuilder">
+ </builder>
+</project-description>
diff --git a/bundles/org.eclipse.team.ui/README b/bundles/org.eclipse.team.ui/README
new file mode 100644
index 000000000..e5bced73f
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/README
@@ -0,0 +1,13 @@
+Note to users of this plug-in
+
+The classes and interfaces in this plug-in are part of an API that is
+still under development and expected to change significantly before
+reaching stability. It is being made available at this early stage to
+solicit feedback from pioneering adopters on the understanding
+that any code that uses this API will almost certainly be broken
+(repeatedly) as the API evolves.
+
+For up-to-date information on these APIs and other related news see
+our component page:
+
+http://dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-vcm-home/main.html \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/about.html b/bundles/org.eclipse.team.ui/about.html
index f3b17d947..9a15e5ca2 100644
--- a/bundles/org.eclipse.team.ui/about.html
+++ b/bundles/org.eclipse.team.ui/about.html
@@ -1,57 +1,30 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<HTML><HEAD><TITLE>About</TITLE>
-<META http-equiv=Content-Type content="text/html; charset=windows-1252">
-<STYLE type=text/css>P {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TABLE {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TD {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-TH {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-PRE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-H2 {
- FONT-WEIGHT: bold; FONT-SIZE: 18pt; LINE-HEIGHT: 14px; FONT-FAMILY: arial, helvetica, geneva
-}
-CODE {
- FONT-SIZE: 10pt; FONT-FAMILY: "Courier New", Courier, mono
-}
-SUP {
- FONT-SIZE: 10px; FONT-FAMILY: arial,helvetica,geneva
-}
-H3 {
- FONT-WEIGHT: bold; FONT-SIZE: 14pt; FONT-FAMILY: arial, helvetica, geneva
-}
-LI {
- FONT-SIZE: 10pt; FONT-FAMILY: arial, helvetica, geneva
-}
-H1 {
- FONT-WEIGHT: bold; FONT-SIZE: 28px; FONT-FAMILY: arial, helvetica, geneva
-}
-BODY {
- MARGIN-TOP: 5mm; FONT-SIZE: 10pt; MARGIN-LEFT: 3mm; FONT-FAMILY: arial, helvetica, geneva
-}
-</STYLE>
-
-<META content="MSHTML 5.50.4522.1800" name=GENERATOR></HEAD>
-<BODY lang=EN-US vLink=purple link=blue>
-<TABLE cellSpacing=5 cellPadding=2 width="100%" border=0>
- <TBODY>
- <TR>
- <TD vAlign=top align=left bgColor=#0080c0 colSpan=2><B><FONT
- face=Arial,Helvetica color=#ffffff>About This Plug-in</FONT></B></TD></TR>
- <TR>
- <TD>
- <P>1st November, 2001</P>
- <H3>License</H3>
- <P>Eclipse.org makes available all content in this plug-in. The plug-in is
- provided to you under the terms and conditions of the <A
- href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License
- Version 0.5</A>. For purposes of the Common Public License, "Program" will
- mean the plug-in.</P></TD></TR></TBODY></TABLE></BODY></HTML>
+<html>
+<head>
+<title>About</title>
+<style type="text/css">
+p, table, td, th { font-family: arial, helvetica, geneva; font-size: 10pt}
+pre { font-family: "Courier New", Courier, mono; font-size: 10pt}
+h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}
+code { font-family: "Courier New", Courier, mono; font-size: 10pt}
+sup { font-family: arial,helvetica,geneva; font-size: 10px}
+h3 { font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}
+li { font-family: arial, helvetica, geneva; font-size: 10pt}
+h1 { font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}
+body { font-family: arial, helvetica, geneva; font-size: 10pt; clip: rect( ); margin-top: 5mm; margin-left: 3mm}
+</style>
+</head>
+<body>
+<body lang=EN-US link=blue vlink=purple>
+<table border=0 cellspacing=5 cellpadding=2 width="100%" >
+ <tr>
+ <td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font color="#FFFFFF" face="Arial,Helvetica">About This Plug-in</font></b></td>
+ </tr>
+ <tr>
+ <td>
+<p>1st November, 2001</p>
+<h3>License</h3>
+<p>Eclipse.org makes available all content in this plug-in. The plug-in is provided to you under the terms and conditions of the
+<a href="http://www.eclipse.org/legal/cpl-v05.html">Common Public License Version 0.5</a>. For purposes of the Common Public License, &quot;Program&quot; will mean the plug-in.</p>
+</td></tr></table>
+</body>
+</html> \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/build.properties b/bundles/org.eclipse.team.ui/build.properties
new file mode 100644
index 000000000..b43fb5e1f
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/build.properties
@@ -0,0 +1,3 @@
+# Eclipse build contribution
+bin.includes=about.html,plugin.xml,plugin.properties,*.jar
+source.teamui.jar=src/
diff --git a/bundles/org.eclipse.team.ui/doc/hglegal.htm b/bundles/org.eclipse.team.ui/doc/hglegal.htm
new file mode 100644
index 000000000..b071dbdf4
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/doc/hglegal.htm
@@ -0,0 +1,14 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.73 [en] (Win98; U) [Netscape]">
+ <title>Legal Notices</title>
+</head>
+<body>
+
+<h3>
+<a NAME="Notices"></a>Notices</h3>
+(c) Copyright IBM Corp. 2000, 2001. All Rights Reserved.
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.ui/doc/ngibmcpy.gif b/bundles/org.eclipse.team.ui/doc/ngibmcpy.gif
new file mode 100644
index 000000000..360f8e998
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/doc/ngibmcpy.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui.html b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui.html
new file mode 100644
index 000000000..18113134f
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui.html
@@ -0,0 +1,30 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
+ <title>Team Core Extension Points</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>Team UI Plug-in</h1></center>
+This document lists all of the extension points that the Team UI plug-in makes available to provider developers.
+<p>
+<hr WIDTH="100%">
+<h1>
+General Extension Points</h1>
+The following extension point can be used to register a configuration mechanism for associating a project
+with a team provider:
+<ul>
+<li>
+<a href="org_eclipse_team_ui_configurationWizards.html">org.eclipse.team.ui.configurationWizards</a></li>
+</ul>
+The following extension point can be used to register a decorator to add team-related adornments to elements
+in the UI:
+<ul>
+<li>
+<a href="org_eclipse_team_ui_decorators.html">org.eclipse.team.ui.decorators</a></li>
+</ul>
+<a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001. All Rights Reserved." BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_configurationWizards.html b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_configurationWizards.html
new file mode 100644
index 000000000..c6e58e2bb
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_configurationWizards.html
@@ -0,0 +1,61 @@
+<!doctype html public "-//w3c//dtd html 4.0//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Eclipse Team UI Extension Point: Configuration Wizards</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+Configuration Wizards</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.team.ui.configurationWizards
+<p><b><i>Description: </i></b>This extension point is used to register
+a method for configuration of a project. Configuration involves the association
+of a project with a team provider, including all information necessary to
+initialize that team provider, including such things as username, password, and
+any relevant information necessary to locate the provider.
+<p>Providers may provide an extension for this extension point, and an
+implementation of <code>org.eclipse.team.ui.IConfigurationWizard</code> which
+gathers the necessary information and configures the project.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT wizard EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST wizard</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; icon&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+
+<ul>
+<li><b>name -</b> the name of the configuration type, as it should appear in the configuration wizard. Examples are "CVS", "WebDAV".</li>
+<li><b>icon -</b> the icon to present in the configuration wizard next to the name.</li>
+<li><b>class -</b> a fully qualified name of the Java class implementing <tt>org.eclipse.team.ui.IConfigurationWizard</tt>.</li>
+<li><b>id - </b> a unique identifier for this extension.</li>
+</ul>
+
+<b><i>Examples:</i></b>
+<p>Following is an example of a configuration wizard extension:
+<p><tt>&nbsp;&nbsp; &lt;extension point="org.eclipse.team.ui.configurationWizard"></tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;wizard</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name="WebDAV"</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; icon="webdav.gif"</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="com.xyz.DAVDecorator"</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id="com.xyz.dav"></tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/wizard></tt>
+<br><tt>&nbsp;&nbsp; &lt;/extension></tt>
+
+<p><b><i>API Information</i>: </b>The value of the <tt>class</tt> attribute
+must represent a class that implements <tt>org.eclipse.team.ui.IConfigurationWizard</tt>.
+This interface supports configuration of a wizard given a workbench and a project.
+
+<p><b><i>Supplied Implementation: </i></b>The plug-in org.eclipse.team.provider.examples.ui contains
+sample implementations of IConfigurationWizard for the WebDAV and filesystem provider types.
+
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001. All Rights Reserved." BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_decorators.html b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_decorators.html
new file mode 100644
index 000000000..9e2ff129b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/doc/org_eclipse_team_ui_decorators.html
@@ -0,0 +1,55 @@
+<!doctype html public "-//w3c//dtd html 4.0//en">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Eclipse Team UI Extension Point: Decorators</title>
+</head>
+<body link="#0000FF" vlink="#800080">
+
+<center>
+<h1>
+Decorators</h1></center>
+<b><i>Identifier: </i></b>org.eclipse.team.ui.decorators
+<p><b><i>Description: </i></b>This extension point is used to register
+object decorator extensions. These decorators may adorn objects with
+labels and icons depending on certain aspects of their team status. For
+example, elements which are locally modified may their label or icon
+modified to indicate this status.
+<p>Providers may provide an extension for this extension point, and an
+implementation of <code>org.eclipse.team.ui.ITeamDecorator</code> which
+decorates objects in a provider-specific fashion.
+<p><b><i>Configuration Markup:</i></b>
+<p><tt>&nbsp;&nbsp; &lt;!ELEMENT decorator EMPTY></tt>
+<br><tt>&nbsp;&nbsp; &lt;!ATTLIST decorator</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; natureId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+CDATA #REQUIRED</tt>
+<br><tt>&nbsp;&nbsp; ></tt>
+
+<ul>
+<li><b>class -</b> a fully qualified name of the Java class implementing <tt>org.eclipse.team.ui.ITeamDecorator>.</li>
+<li><b>natureId</b> - a name of the nature, projects having which will be decorated by this decorator.</li>
+</ul>
+
+<b><i>Examples:</i></b>
+<p>Following is an example of decorator configuration:
+<p><tt>&nbsp;&nbsp; &lt;extension point="org.eclipse.team.ui.decorators"></tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;decorator</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="com.xyz.XYZDecorator"</tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; natureId="com.xyz.XYZNature"></tt>
+<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/decorator></tt>
+<br><tt>&nbsp;&nbsp; &lt;/extension></tt>
+
+<p><b><i>API Information</i>: </b>The value of the <tt>class</tt> attribute
+must represent a class that implements <tt>org.eclipse.team.ui.ITeamDecorator</tt>.
+This decorator may be called upon at any time to provide textual labels and
+icon adornments for a given resource.
+
+<p><b><i>Supplied Implementation: </i></b>The plug-in org.eclipse.team.provider.examples.ui contains
+sample implementations of ITeamDecorator which decorate for the filesystem provider implementation
+and the WebDAV provider implementation.
+
+<p><a href="hglegal.htm"><img SRC="ngibmcpy.gif" ALT="Copyright IBM Corp. 2000, 2001. All Rights Reserved." BORDER=0 height=12 width=195></a>
+</body>
+</html>
diff --git a/bundles/org.eclipse.team.ui/icons/full/ovr/checkedin_ov.gif b/bundles/org.eclipse.team.ui/icons/full/ovr/checkedin_ov.gif
new file mode 100644
index 000000000..871a2a5db
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/icons/full/ovr/checkedin_ov.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.ui/icons/full/ovr/checkedout_ov.gif b/bundles/org.eclipse.team.ui/icons/full/ovr/checkedout_ov.gif
new file mode 100644
index 000000000..2993469b3
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/icons/full/ovr/checkedout_ov.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.ui/icons/full/ovr/dirty_ov.gif b/bundles/org.eclipse.team.ui/icons/full/ovr/dirty_ov.gif
new file mode 100644
index 000000000..8b6177de1
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/icons/full/ovr/dirty_ov.gif
Binary files differ
diff --git a/bundles/org.eclipse.team.ui/plugin.properties b/bundles/org.eclipse.team.ui/plugin.properties
new file mode 100644
index 000000000..771c5e719
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/plugin.properties
@@ -0,0 +1,26 @@
+pluginName=Eclipse Team Support UI
+
+ASCIIPreferencePage.name=ASCII Resources
+
+ConfigureProject.label=Connect Project to Team...
+ConfigureProject.tooltip=Connect Project to Team
+
+DeconfigureProject.label=Disconnect Project from Team...
+DeconfigureProject.tooltip=Disconnect Project from Team
+
+Delete.label=&Delete
+Delete.tooltip=Delete the local resource and remote resource
+
+Manage.label=&Manage
+Manage.tooltip=Consider this resource for team operations
+
+Move.label=Mo&ve...
+Move.tooltip=Move the local resource and the remote resource
+
+Team=Team
+TeamGroupMenu.label=Team
+
+ToggleNavigatorDecorations.label=Show Team Decorations
+
+Unmanage.label=&Unmanage
+Unmanage.tooltip=Do not consider this resource for team operations
diff --git a/bundles/org.eclipse.team.ui/plugin.xml b/bundles/org.eclipse.team.ui/plugin.xml
new file mode 100644
index 000000000..cf632295f
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/plugin.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- File written by PDE 1.0 -->
+<plugin
+ id="org.eclipse.team.ui"
+ name="%pluginName"
+ version="2.0.0"
+ provider-name="Object Technology International, Inc."
+ class="org.eclipse.team.ui.TeamUIPlugin">
+<requires>
+ <import plugin="org.eclipse.core.resources"/>
+ <import plugin="org.eclipse.team.core"/>
+ <import plugin="org.eclipse.ui"/>
+</requires>
+
+<runtime>
+ <library name="teamui.jar">
+ <export name="*"/>
+ </library>
+</runtime>
+
+<extension-point id="configurationWizards" name="Configuration Wizards"/>
+<extension-point id="decorators" name="Provider Decorators"/>
+
+<!-- Define some example ASCII extensions. This will be done
+ by other plugins later. -->
+
+<extension point="org.eclipse.team.core.fileTypes">
+ <fileTypes extension="txt" key="isAscii" value="true"/>
+ <fileTypes extension="java" key="isAscii" value="true"/>
+ <fileTypes extension="properties" key="isAscii" value="true"/>
+ <fileTypes extension="xml" key="isAscii" value="true"/>
+</extension>
+
+<!-- **************** PREFERENCES ******************* -->
+ <extension
+ point = "org.eclipse.ui.preferencePages">
+ <page id="org.eclipse.team.ui.ASCIIPreferences"
+ class="org.eclipse.team.internal.ui.ASCIIPreferencePage"
+ name="%ASCIIPreferencePage.name">
+ </page>
+ </extension>
+
+ <extension point="org.eclipse.ui.viewActions">
+ <viewContribution
+ id="org.eclipse.team.ui.navcontribution"
+ targetID="org.eclipse.ui.views.ResourceNavigator">
+ <action id="org.eclipse.team.ui.infoinnav"
+ label="%ToggleNavigatorDecorations.label"
+ menubarPath="additions"
+ state="false"
+ tooltip="%ToggleNavigatorVersionLabels.tooltip"
+ class="org.eclipse.team.ui.actions.ToggleNavigatorDecorations">
+ </action>
+ </viewContribution>
+ </extension>
+
+<!-- ****************** POPUP ACTIONS *************** -->
+
+<extension
+ point="org.eclipse.ui.popupMenus">
+ <objectContribution
+ id="org.eclipse.team.ui.ProjectContributions"
+ objectClass="org.eclipse.core.resources.IProject">
+ <menu
+ id="team.main"
+ path="additions"
+ label="%TeamGroupMenu.label">
+ <separator name="checkinGroup"/>
+ <separator name="manageGroup"/>
+ <separator name="advancedGroup"/>
+ <separator name="projectGroup"/>
+ </menu>
+ <action
+ id="org.eclipse.team.ui.DeconfigureProject"
+ label="%DeconfigureProject.label"
+ tooltip="%DeconfigureProject.tooltip"
+ menubarPath="team.main/projectGroup"
+ enablesFor="1"
+ class="org.eclipse.team.ui.actions.DeconfigureProjectAction">
+ </action>
+ <action
+ id="org.eclipse.team.ui.ConfigureProject"
+ label="%ConfigureProject.label"
+ tooltip="%ConfigureProject.tooltip"
+ menubarPath="team.main/projectGroup"
+ enablesFor="1"
+ class="org.eclipse.team.ui.actions.ConfigureProjectAction">
+ </action>
+ </objectContribution>
+
+ <objectContribution
+ id="org.eclipse.team.ui.ResourceContributions"
+ objectClass="org.eclipse.core.resources.IResource">
+ <menu
+ id="team.main"
+ path="additions"
+ label="%TeamGroupMenu.label">
+ <separator name="checkinGroup"/>
+ <separator name="manageGroup"/>
+ <separator name="advancedGroup"/>
+ <separator name="projectGroup"/>
+ </menu>
+ </objectContribution>
+
+ <objectContribution
+ id="org.eclipse.team.ui.FileContributions"
+ objectClass="org.eclipse.core.resources.IFile">
+ <menu
+ id="team.main"
+ path="additions"
+ label="%TeamGroupMenu.label">
+ <separator name="checkinGroup"/>
+ <separator name="manageGroup"/>
+ <separator name="advancedGroup"/>
+ <separator name="projectGroup"/>
+ </menu>
+ <action
+ id="org.eclipse.team.ui.Delete"
+ label="%Delete.label"
+ tooltip="%Delete.tooltip"
+ menubarPath="team.main/advancedGroup"
+ class="org.eclipse.team.ui.actions.DeleteAction">
+ </action>
+ <action
+ id="org.eclipse.team.ui.Move"
+ label="%Move.label"
+ tooltip="%Move.tooltip"
+ menubarPath="team.main/advancedGroup"
+ class="org.eclipse.team.ui.actions.MoveAction">
+ </action>
+ </objectContribution>
+
+ <objectContribution
+ id="org.eclipse.team.ui.FolderContributions"
+ objectClass="org.eclipse.core.resources.IFolder">
+ <menu
+ id="team.main"
+ path="additions"
+ label="%TeamGroupMenu.label">
+ <separator name="checkinGroup"/>
+ <separator name="manageGroup"/>
+ <separator name="advancedGroup"/>
+ <separator name="projectGroup"/>
+ </menu>
+ <action
+ id="org.eclipse.team.ui.Delete"
+ label="%Delete.label"
+ tooltip="%Delete.tooltip"
+ menubarPath="team.main/advancedGroup"
+ class="org.eclipse.team.ui.actions.DeleteAction">
+ </action>
+ <action
+ id="org.eclipse.team.ui.Move"
+ label="%Move.label"
+ tooltip="%Move.tooltip"
+ menubarPath="team.main/advancedGroup"
+ class="org.eclipse.team.ui.actions.MoveAction">
+ </action>
+ </objectContribution>
+</extension>
+
+</plugin>
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ASCIIPreferencePage.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ASCIIPreferencePage.java
new file mode 100644
index 000000000..4312db427
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ASCIIPreferencePage.java
@@ -0,0 +1 @@
+package org.eclipse.team.internal.ui; /* * (c) Copyright IBM Corp. 2000, 2001. * All Rights Reserved. */ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.ComboBoxCellEditor; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.TableLayout; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.team.core.IFileTypeRegistry; import org.eclipse.team.core.TeamPlugin; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; /** * This preference page displays all patterns which determine whether a resource * is to be treated as an ASCII file or not. The page allows the user to add or * remove entries from this table, and change their values from ASCII to Binary. */ public class ASCIIPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { // Some string constants for display purposes private static final String ASCII = Policy.bind("ASCIIPreferencePage.ascii"); private static final String BINARY = Policy.bind("ASCIIPreferencePage.binary"); private static final String TRUE = "true"; private static final String FALSE = "false"; // The name of the parameter in the file registry private static final String IS_ASCII = "isAscii"; // The input for the table viewer private List input; // Widgets private TableViewer viewer; private Button removeButton; private Button changeButton; // File type registry private IFileTypeRegistry registry; /** * TableEntry is a pair of strings representing an entry in the table */ class TableEntry { String ext; String value; public TableEntry(String ext, String value) { this.ext = ext; this.value = value; } public String getExtension() { return ext; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } /** * TableLabelProvider provides labels for TableEntrys. */ class TableLabelProvider extends LabelProvider implements ITableLabelProvider { public String getColumnText(Object element, int columnIndex) { TableEntry entry = (TableEntry)element; switch (columnIndex) { case 0: return entry.getExtension(); case 1: return entry.getValue(); default: return null; } } public Image getColumnImage(Object element, int columnIndex) { return null; } }; /* * Method declared on IWorkbenchPreferencePage */ public void init(IWorkbench workbench) { } /** * @see PreferencePage#createControl */ protected Control createContents(Composite ancestor) { noDefaultAndApplyButton(); Composite parent = new Composite(ancestor, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; parent.setLayout(layout); // set F1 help //WorkbenchHelp.setHelp(parent, new DialogPageContextComputer (this, ITeamHelpContextIds.ASCII_PREFERENCE_PAGE)); Label l1 = new Label(parent, SWT.NULL); l1.setText(Policy.bind("ASCIIPreferencePage.description")); GridData data = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); data.horizontalSpan = 2; l1.setLayoutData(data); viewer = new TableViewer(parent, SWT.MULTI | SWT.FULL_SELECTION | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); Table table = viewer.getTable(); new TableEditor(table); table.setHeaderVisible(true); table.setLinesVisible(true); GridData gd = new GridData(GridData.FILL_BOTH); gd.widthHint = convertWidthInCharsToPixels(30); table.setLayoutData(gd); table.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { handleSelection(); } }); // Create the table columns new TableColumn(table, SWT.NULL); new TableColumn(table, SWT.NULL); TableColumn[] columns = table.getColumns(); columns[0].setText(Policy.bind("ASCIIPreferencePage.extension")); columns[1].setText(Policy.bind("ASCIIPreferencePage.contents")); CellEditor editor = new ComboBoxCellEditor(table, new String[] {ASCII, BINARY}); viewer.setCellEditors(new CellEditor[] {null, editor}); viewer.setColumnProperties(new String[] {"extension", "contents"}); viewer.setCellModifier(new ICellModifier() { public Object getValue(Object element, String property) { String value = ((TableEntry)element).getValue(); if (value.equals(ASCII)) { return new Integer(0); } else { return new Integer(1); } } public boolean canModify(Object element, String property) { return true; } public void modify(Object element, String property, Object value) { IStructuredSelection selection = (IStructuredSelection)viewer.getSelection(); TableEntry entry = (TableEntry)selection.getFirstElement(); if (((Integer)value).intValue() == 0) { entry.setValue(ASCII); } else { entry.setValue(BINARY); } viewer.refresh(entry); } }); viewer.setLabelProvider(new TableLabelProvider()); viewer.setContentProvider(new IStructuredContentProvider() { public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } public Object[] getElements(Object inputElement) { if (inputElement == null) return null; return ((List)inputElement).toArray(); } }); viewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { ISelection selection = event.getSelection(); if (selection == null || !(selection instanceof IStructuredSelection)) { return; } viewer.editElement(((IStructuredSelection)selection).getFirstElement(), 1); } }); TableLayout tl = new TableLayout(); tl.addColumnData(new ColumnWeightData(50)); tl.addColumnData(new ColumnWeightData(50)); table.setLayout(tl); Composite buttons = new Composite(parent, SWT.NULL); buttons.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); buttons.setLayout(new GridLayout()); Button addButton = new Button(buttons, SWT.PUSH); addButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); addButton.setText(Policy.bind("ASCIIPreferencePage.add")); addButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { addPattern(); } }); removeButton= new Button(buttons, SWT.PUSH); removeButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); removeButton.setText(Policy.bind("ASCIIPreferencePage.remove")); removeButton.setEnabled(false); removeButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { removePattern(); } }); changeButton = new Button(buttons, SWT.PUSH); changeButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); changeButton.setText(Policy.bind("ASCIIPreferencePage.change")); changeButton.setEnabled(false); changeButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event e) { changePattern(); } }); fillTable(); return parent; } /** * Do anything necessary because the OK button has been pressed. * * @return whether it is okay to close the preference page */ public boolean performOk() { Iterator it = input.iterator(); while (it.hasNext()) { TableEntry entry = (TableEntry)it.next(); String value = entry.getValue(); if (value.equals(ASCII)) { value = TRUE; } else { value = FALSE; } registry.setValue(entry.getExtension(), IS_ASCII, value); } return true; } /** * Fill the table with the values from the file type registry */ private void fillTable() { this.input = new ArrayList(); this.registry = TeamPlugin.getFileTypeRegistry(); String[] extensions = registry.getExtensions(IS_ASCII); for (int i = 0; i < extensions.length; i++) { String value = registry.getValue(extensions[i], IS_ASCII); if (value.equals(TRUE)) { value = ASCII; } else { value = BINARY; } input.add(new TableEntry(extensions[i], value)); } viewer.setInput(input); } /** * Add a new item to the table with the default type of ASCII. */ private void addPattern() { InputDialog dialog = new InputDialog(getShell(), Policy.bind("ASCIIPreferencePage.enterExtensionShort"), Policy.bind("ASCIIPreferencePage.enterExtensionLong"), null, null); dialog.open(); if (dialog.getReturnCode() != InputDialog.OK) return; String pattern = dialog.getValue(); if (pattern.equals("")) return; // Check if the item already exists Iterator it = input.iterator(); while (it.hasNext()) { TableEntry entry = (TableEntry)it.next(); if (entry.getExtension().equals(pattern)) { MessageDialog.openWarning(getShell(), Policy.bind("ASCIIPreferencePage.extensionExistsShort"), Policy.bind("ASCIIPreferencePage.extensionExistsLong")); return; } } input.add(new TableEntry(pattern, ASCII)); viewer.refresh(); } /** * Remove the selected items from the table */ private void removePattern() { ISelection selection = viewer.getSelection(); if (selection == null || !(selection instanceof IStructuredSelection)) { return; } IStructuredSelection ss = (IStructuredSelection)selection; Iterator it = ss.iterator(); while (it.hasNext()) { TableEntry entry = (TableEntry)it.next(); input.remove(entry); } viewer.refresh(); } /** * Toggle the selected items' content types */ private void changePattern() { ISelection selection = viewer.getSelection(); if (selection == null || !(selection instanceof IStructuredSelection)) { return; } IStructuredSelection ss = (IStructuredSelection)selection; Iterator it = ss.iterator(); while (it.hasNext()) { TableEntry entry = (TableEntry)it.next(); String string = entry.getValue(); if (string.equals(ASCII)) { entry.setValue(BINARY); } else { entry.setValue(ASCII); } viewer.refresh(entry); } } /** * The table viewer selection has changed. Update the remove and change button enablement. */ private void handleSelection() { boolean empty = viewer.getSelection().isEmpty(); removeButton.setEnabled(!empty); changeButton.setEnabled(!empty); } } \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardElement.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardElement.java
new file mode 100644
index 000000000..bb7c9e813
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardElement.java
@@ -0,0 +1,137 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.internal.model.WorkbenchAdapter;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.team.ui.*;
+
+/**
+ * ConfigurationWizardElement represents an item in the configuration wizard table,
+ * declared by an extension to the configurationWizards extension point.
+ */
+public class ConfigurationWizardElement extends WorkbenchAdapter implements IAdaptable {
+ private String id;
+ private String name;
+ private ImageDescriptor imageDescriptor;
+ private String description;
+ private IConfigurationElement configurationElement;
+
+ /**
+ * Creates a new instance of this class
+ *
+ * @param name the name of the element
+ */
+ public ConfigurationWizardElement(String name) {
+ this.name = name;
+ }
+ /**
+ * Create an the instance of the object described by the configuration
+ * element. That is, create the instance of the class the isv supplied in
+ * the extension point.
+ *
+ * @throws CoreException if an error occurs creating the extension
+ */
+ public Object createExecutableExtension() throws CoreException {
+ return TeamUIPlugin.createExtension(configurationElement, ConfigureProjectWizard.ATT_CLASS);
+ }
+ /*
+ * Method declared on IAdaptable.
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) {
+ return this;
+ }
+ return Platform.getAdapterManager().getAdapter(this, adapter);
+ }
+ /**
+ * Returns the configuration element
+ *
+ * @return the configuration element
+ */
+ public IConfigurationElement getConfigurationElement() {
+ return configurationElement;
+ }
+ /**
+ * Returns the description parameter of this element
+ *
+ * @return the description of this elemnet
+ */
+ public String getDescription() {
+ return description;
+ }
+ /**
+ * Returns the image for the given element
+ *
+ * @param element the element to get the image for
+ * @return the image for the given element
+ */
+ public ImageDescriptor getImageDescriptor(Object element) {
+ return imageDescriptor;
+ }
+ /**
+ * Returns the label for the given element
+ *
+ * @param element the element to get the label for
+ * @return the label for the given element
+ */
+ public String getLabel(Object element) {
+ return name;
+ }
+ /**
+ * Returns the id as specified in the extension.
+ *
+ * @return java.lang.String
+ */
+ public String getID() {
+ return id;
+ }
+ /**
+ * Returns the image for this element.
+ *
+ * @return the image for this element
+ */
+ public ImageDescriptor getImageDescriptor() {
+ return imageDescriptor;
+ }
+ /**
+ * Set the configuration element
+ *
+ * @param newConfigurationElement the new configuration element
+ */
+ public void setConfigurationElement(IConfigurationElement newConfigurationElement) {
+ configurationElement = newConfigurationElement;
+ }
+ /**
+ * Set the description parameter of this element
+ *
+ * @param value the new desrciption
+ */
+ public void setDescription(String value) {
+ description = value;
+ }
+ /**
+ * Sets the id parameter of this element
+ *
+ * @param value the new ID
+ */
+ public void setID(String value) {
+ id = value;
+ }
+ /**
+ * Sets the image for this element.
+ *
+ * @param value the new image
+ */
+ public void setImageDescriptor(ImageDescriptor value) {
+ imageDescriptor = value;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardNode.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardNode.java
new file mode 100644
index 000000000..1079aa80c
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigurationWizardNode.java
@@ -0,0 +1,65 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardNode;
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * ConfigurationWizardNode represents the objects in the
+ * table in the Configuration wizard.
+ */
+public class ConfigurationWizardNode implements IWizardNode {
+ // The element this node represents
+ ConfigurationWizardElement element;
+ // The wizard this node is in
+ IWizard wizard;
+
+ /**
+ * Create a new ConfigurationWizardNode
+ *
+ * @param element the configuration wizard element for this node
+ */
+ public ConfigurationWizardNode(ConfigurationWizardElement element) {
+ this.element = element;
+ }
+ /*
+ * Method declared on IWizardNode.
+ */
+ public void dispose() {
+ if (wizard != null) {
+ wizard.dispose();
+ wizard = null;
+ }
+ }
+ /*
+ * Method declared on IWizardNode.
+ */
+ public Point getExtent() {
+ return new Point(-1, -1);
+ }
+ /*
+ * Method declared on IWizardNode.
+ */
+ public IWizard getWizard() {
+ if (wizard == null) {
+ try {
+ wizard = (IWizard)element.createExecutableExtension();
+ } catch (CoreException e) {
+ System.out.println(Policy.bind("exceptionCreatingWizard"));
+ }
+ }
+ return wizard;
+ }
+ /*
+ * Method declared on IWizardNode.
+ */
+ public boolean isContentCreated() {
+ return wizard != null;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizard.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizard.java
new file mode 100644
index 000000000..e16bb3726
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizard.java
@@ -0,0 +1,147 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IPluginRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.team.ui.IConfigurationWizard;
+import org.eclipse.team.ui.TeamUIPlugin;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.internal.model.AdaptableList;
+
+/**
+ * The wizard for associating projects with team providers
+ */
+public class ConfigureProjectWizard extends Wizard implements IConfigurationWizard {
+ private IWorkbench workbench;
+ private IProject project;
+
+ private ConfigureProjectWizardMainPage mainPage;
+ private String pluginId = UIConstants.PLUGIN_ID;
+ private String extensionPoint = UIConstants.PT_CONFIGURATION;
+
+ protected final static String TAG_WIZARD = "wizard";
+ protected final static String TAG_DESCRIPTION = "description";
+ protected final static String ATT_NAME = "name";
+ protected final static String ATT_CLASS = "class";
+ protected final static String ATT_ICON = "icon";
+ protected final static String ATT_ID = "id";
+
+ /**
+ * @see Wizard#addPages
+ */
+ public void addPages() {
+ mainPage = new ConfigureProjectWizardMainPage("configurePage1", Policy.bind("ConfigureProjectWizard.configureProject"), null, getAvailableWizards());
+ mainPage.setDescription(Policy.bind("ConfigureProjectWizard.description"));
+ mainPage.setProject(project);
+ mainPage.setWorkbench(workbench);
+ addPage(mainPage);
+ }
+ /**
+ * @see Wizard#performFinish
+ */
+ public boolean performFinish() {
+ return true;
+ }
+ /**
+ * Returns the configuration wizards that are available for invocation.
+ *
+ * @return the available wizards
+ */
+ AdaptableList getAvailableWizards() {
+ AdaptableList result = new AdaptableList();
+ IPluginRegistry registry = Platform.getPluginRegistry();
+ IExtensionPoint point = registry.getExtensionPoint(pluginId, extensionPoint);
+ if (point != null) {
+ IExtension[] extensions = point.getExtensions();
+// extensions = orderExtensions(extensions);
+ for (int i = 0; i < extensions.length; i++) {
+ IConfigurationElement[] elements = extensions[i].getConfigurationElements();
+ for (int j = 0; j < elements.length; j++) {
+ IConfigurationElement element = elements[j];
+ if (element.getName().equals(TAG_WIZARD)) {
+ ConfigurationWizardElement wizard = createWizardElement(element);
+ if (wizard != null) {
+ result.add(wizard);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ /**
+ * Returns a new ConfigurationWizardElement configured according to the parameters
+ * contained in the passed Registry.
+ *
+ * May answer null if there was not enough information in the Extension to create
+ * an adequate wizard
+ *
+ * @param element the element for which to create a wizard element
+ * @return the wizard element for the given element
+ */
+ protected ConfigurationWizardElement createWizardElement(IConfigurationElement element) {
+ // WizardElements must have a name attribute
+ String nameString = element.getAttribute(ATT_NAME);
+ if (nameString == null) {
+ // Missing attribute
+ return null;
+ }
+ ConfigurationWizardElement result = new ConfigurationWizardElement(nameString);
+ if (initializeWizard(result, element)) {
+ // initialization was successful
+ return result;
+ }
+ return null;
+ }
+ /**
+ * Initialize the passed element's properties based on the contents of
+ * the passed registry. Answer a boolean indicating whether the element
+ * was able to be adequately initialized.
+ *
+ * @param element the element to initialize the properties for
+ * @param extension the registry to get properties from
+ * @return whether initialization was successful
+ */
+ protected boolean initializeWizard(ConfigurationWizardElement element, IConfigurationElement config) {
+ element.setID(config.getAttribute(ATT_ID));
+ String description = "";
+ IConfigurationElement [] children = config.getChildren(TAG_DESCRIPTION);
+ if (children.length >= 1) {
+ description = children[0].getValue();
+ }
+
+ element.setDescription(description);
+
+ // apply CLASS and ICON properties
+ element.setConfigurationElement(config);
+ String iconName = config.getAttribute(ATT_ICON);
+ if (iconName != null) {
+ IExtension extension = config.getDeclaringExtension();
+ element.setImageDescriptor(TeamUIPlugin.getImageDescriptorFromExtension(extension, iconName));
+ }
+ // ensure that a class was specified
+ if (element.getConfigurationElement() == null) {
+ // Missing attribute
+ return false;
+ }
+ setForcePreviousAndNextButtons(true);
+ return true;
+ }
+ /*
+ * Method declared on IConfigurationWizard
+ */
+ public void init(IWorkbench workbench, IProject project) {
+ this.workbench = workbench;
+ this.project = project;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizardMainPage.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizardMainPage.java
new file mode 100644
index 000000000..f6e631356
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ConfigureProjectWizardMainPage.java
@@ -0,0 +1,141 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.team.ui.IConfigurationWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.internal.model.AdaptableList;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+/**
+ * The main page of the configure project wizard. It contains a table
+ * which lists possible team providers with which to configure the project.
+ * The user may select one and press "Next", which will display a provider-
+ * specific wizard page.
+ */
+public class ConfigureProjectWizardMainPage extends WizardPage {
+ private Table table;
+ private TableViewer viewer;
+ private AdaptableList wizards;
+ private IWorkbench workbench;
+ private IProject project;
+
+ private ConfigurationWizardElement selectedElement;
+
+ /**
+ * Create a new ConfigureProjectWizardMainPage
+ *
+ * @param pageName the name of the page
+ * @param title the title of the page
+ * @param titleImage the image for the page title
+ * @param wizard the wizards to populate the table with
+ */
+ public ConfigureProjectWizardMainPage(String pageName, String title, ImageDescriptor titleImage, AdaptableList wizards) {
+ super(pageName, title, titleImage);
+ this.wizards = wizards;
+ }
+ /**
+ * @see WizardPage#canFlipToNextPage
+ */
+ public boolean canFlipToNextPage() {
+ return selectedElement != null;
+ }
+ /**
+ * @see WizardPage#createControl
+ */
+ public void createControl(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NULL);
+ composite.setLayout(new GridLayout());
+ composite.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ setControl(composite);
+
+ Label label = new Label(composite, SWT.LEFT);
+ label.setText(Policy.bind("ConfigureProjectWizardMainPage.selectRepository"));
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ label.setLayoutData(data);
+
+ table = new Table(composite, SWT.SINGLE | SWT.BORDER);
+ data = new GridData(GridData.FILL_BOTH);
+ data.heightHint = table.getItemHeight() * 7;
+ table.setLayoutData(data);
+ viewer = new TableViewer(table);
+ viewer.setContentProvider(new WorkbenchContentProvider());
+ viewer.setLabelProvider(new WorkbenchLabelProvider());
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection selection = event.getSelection();
+ if (selection == null || !(selection instanceof IStructuredSelection)) {
+ selectedElement = null;
+ setPageComplete(false);
+ return;
+ }
+ IStructuredSelection ss = (IStructuredSelection)selection;
+ if (ss.size() != 1) {
+ selectedElement = null;
+ setPageComplete(false);
+ return;
+ }
+ selectedElement = (ConfigurationWizardElement)ss.getFirstElement();
+ setPageComplete(true);
+ }
+ });
+ viewer.setInput(wizards);
+ }
+ /**
+ * The <code>WizardSelectionPage</code> implementation of
+ * this <code>IWizardPage</code> method returns the first page
+ * of the currently selected wizard if there is one.
+ *
+ * @see WizardPage#getNextPage
+ */
+ public IWizardPage getNextPage() {
+ IConfigurationWizard wizard;
+ try {
+ wizard = (IConfigurationWizard)selectedElement.createExecutableExtension();
+ wizard.init(workbench, project);
+ } catch (CoreException e) {
+ System.out.println(Policy.bind("exceptionCreatingWizard"));
+ return null;
+ }
+ wizard.addPages();
+ return wizard.getStartingPage();
+ }
+ /**
+ * Set the workbench to the argument
+ *
+ * @param workbench the workbench to set
+ */
+ public void setWorkbench(IWorkbench workbench) {
+ this.workbench = workbench;
+ }
+ /**
+ * Set the project to the argument
+ *
+ * @param project the project to set
+ */
+ public void setProject(IProject project) {
+ this.project = project;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/OverlayIcon.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/OverlayIcon.java
new file mode 100644
index 000000000..19c0b3dfe
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/OverlayIcon.java
@@ -0,0 +1,144 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+
+/**
+ * An OverlayIcon consists of a main icon and several adornments.
+ */
+public class OverlayIcon extends CompositeImageDescriptor {
+ // Constants for default size
+ static final int DEFAULT_WIDTH = 22;
+ static final int DEFAULT_HEIGHT = 16;
+
+ // The size of this icon
+ private Point size = null;
+
+ // The base image
+ private ImageData base;
+ // All overlay images
+ private ImageDescriptor overlays[][];
+
+ /**
+ * OverlayIcon constructor
+ *
+ * @param base the base image
+ * @param overlays the overlay images
+ * @param size the size of the icon
+ */
+ public OverlayIcon(ImageData base, ImageDescriptor[][] overlays, Point size) {
+ this.base = base;
+ this.overlays = overlays;
+ this.size = size;
+ }
+ /**
+ * Draws the overlays in the bottom left
+ *
+ * @param overlays the overlay images
+ */
+ protected void drawBottomLeft(ImageDescriptor[] overlays) {
+ if (overlays == null) return;
+ int length = overlays.length;
+ int x = 0;
+ for (int i= 0; i < 3; i++) {
+ if (i < length && overlays[i] != null) {
+ ImageData id = overlays[i].getImageData();
+ drawImage(id, x, getSize().y - id.height);
+ //x += id.width;
+ }
+ }
+ }
+ /**
+ * Draws the overlays in the bottom right
+ *
+ * @param overlays the overlay images
+ */
+ protected void drawBottomRight(ImageDescriptor[] overlays) {
+ if (overlays == null) return;
+ int length = overlays.length;
+ int x = getSize().x;
+ for (int i= 2; i >= 0; i--) {
+ if (i < length && overlays[i] != null) {
+ ImageData id = overlays[i].getImageData();
+ //x -= id.width;
+ drawImage(id, x, getSize().y - id.height);
+ }
+ }
+ }
+ /**
+ * @see CompositeImage#fill
+ */
+ protected void drawCompositeImage(int width, int height) {
+ ImageData bg = base;
+ if (bg == null) {
+ bg = DEFAULT_IMAGE_DATA;
+ }
+ drawImage(bg, 0, 0);
+
+ if (overlays != null) {
+ if (overlays.length > 0) {
+ drawTopRight(overlays[0]);
+ }
+
+ if (overlays.length > 1) {
+ drawBottomRight(overlays[1]);
+ }
+
+ if (overlays.length > 2) {
+ drawBottomLeft(overlays[2]);
+ }
+
+ if (overlays.length > 3) {
+ drawTopLeft(overlays[3]);
+ }
+ }
+ }
+ /**
+ * Draws the overlays in the top left
+ *
+ * @param overlays the overlay images
+ */
+ protected void drawTopLeft(ImageDescriptor[] overlays) {
+ if (overlays == null) return;
+ int length = overlays.length;
+ int x = 0;
+ for (int i= 0; i < 3; i++) {
+ if (i < length && overlays[i] != null) {
+ ImageData id = overlays[i].getImageData();
+ drawImage(id, x, 0);
+ //x += id.width;
+ }
+ }
+ }
+ /**
+ * Draws the overlays in the top right
+ *
+ * @param overlays the overlay images
+ */
+ protected void drawTopRight(ImageDescriptor[] overlays) {
+ if (overlays == null) return;
+ int length = overlays.length;
+ //int x = getSize().x;
+ int x = 0;
+ for (int i = 8; i >= 0; i--) {
+ if (i < length && overlays[i] != null) {
+ ImageData id = overlays[i].getImageData();
+ //x -= id.width;
+ drawImage(id, x, 0);
+ }
+ }
+ }
+ /**
+ * @see CompositeImage#getSize
+ */
+ protected Point getSize() {
+ return size;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/Policy.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/Policy.java
new file mode 100644
index 000000000..a38a7c6a3
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/Policy.java
@@ -0,0 +1,117 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+
+/**
+ * Policy implements NLS convenience methods for the plugin and
+ * makes progress monitor policy decisions
+ */
+public class Policy {
+ // The resource bundle to get strings from
+ protected static ResourceBundle bundle = null;
+
+ /**
+ * Creates a NLS catalog for the given locale.
+ *
+ * @param bundleName the name of the bundle
+ */
+ public static void localize(String bundleName) {
+ bundle = ResourceBundle.getBundle(bundleName);
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given string.
+ *
+ * @param id the id to look up
+ * @param binding the string to bind to the result
+ * @return the bound string
+ */
+ public static String bind(String id, String binding) {
+ return bind(id, new String[] { binding });
+ }
+
+ /**
+ * Lookup the message with the given ID in this catalog and bind its
+ * substitution locations with the given strings.
+ *
+ * @param id the id to look up
+ * @param binding1 the first string to bind to the result
+ * @param binding2 the second string to bind to the result
+ * @return the bound string
+ */
+ public static String bind(String id, String binding1, String binding2) {
+ return bind(id, new String[] { binding1, binding2 });
+ }
+
+ /**
+ * Gets a string from the resource bundle. We don't want to crash because of a missing String.
+ * Returns the key if not found.
+ *
+ * @param key the id to look up
+ * @return the string with the given key
+ */
+ public static String bind(String key) {
+ try {
+ return bundle.getString(key);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Gets a string from the resource bundle and binds it with the given arguments. If the key is
+ * not found, return the key.
+ *
+ * @param key the id to look up
+ * @param args the strings to bind to the result
+ * @return the bound string
+ */
+ public static String bind(String key, Object[] args) {
+ try {
+ return MessageFormat.format(bind(key), args);
+ } catch (MissingResourceException e) {
+ return key;
+ } catch (NullPointerException e) {
+ return "!" + key + "!";
+ }
+ }
+
+ /**
+ * Checks if the progress monitor is canceled.
+ *
+ * @param monitor the onitor to check for cancellation
+ * @throws OperationCanceledException if the monitor is canceled
+ */
+ public static void checkCanceled(IProgressMonitor monitor) {
+ if (monitor.isCanceled()) {
+ throw new OperationCanceledException();
+ }
+ }
+ /**
+ * Returns a monitor for the given monitor
+ *
+ * @param monitor the monitor to return a monitor for
+ * @return a monitor for the given monitor
+ */
+ public static IProgressMonitor monitorFor(IProgressMonitor monitor) {
+ if (monitor == null) {
+ return new NullProgressMonitor();
+ }
+ return monitor;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamResourceDecorator.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamResourceDecorator.java
new file mode 100644
index 000000000..97760e78a
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamResourceDecorator.java
@@ -0,0 +1,336 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IPluginRegistry;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.core.IResourceStateChangeListener;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.ui.ITeamDecorator;
+import org.eclipse.team.ui.TeamUIPlugin;
+
+/**
+ * TeamResourceDecorator is a general decorator for team items in a view.
+ */
+public class TeamResourceDecorator extends LabelProvider implements ILabelDecorator, IResourceChangeListener, IResourceStateChangeListener {
+ // Constants
+ protected final static String TAG_DECORATOR = "decorator";
+ protected final static String ATT_CLASS = "class";
+ protected final static String ATT_NATUREID = "natureId";
+
+ // The shell this decorator is in
+ private Shell shell;
+
+ // The decorator registry.
+ // key = natureId
+ // value = decorator instance
+ Map decorators = new HashMap();
+
+ /**
+ * Creates a new decorator with the given shell. The shell is
+ * needed for determining the UI display for updates.
+ *
+ * @param shell the shell the decorator is in.
+ */
+ public TeamResourceDecorator(Shell shell) {
+ this.shell = shell;
+ initializeDecorators();
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_AUTO_BUILD);
+ TeamPlugin.getManager().addResourceStateChangeListener(this);
+ }
+
+ /**
+ * Initialize the decorators table
+ */
+ void initializeDecorators() {
+ IPluginRegistry registry = Platform.getPluginRegistry();
+ IExtensionPoint point = registry.getExtensionPoint(TeamUIPlugin.ID, UIConstants.PT_DECORATORS);
+ if (point != null) {
+ IExtension[] extensions = point.getExtensions();
+ for (int i = 0; i < extensions.length; i++) {
+ IConfigurationElement[] elements = extensions[i].getConfigurationElements();
+ for (int j = 0; j < elements.length; j++) {
+ IConfigurationElement element = elements[j];
+ if (element.getName().equals(TAG_DECORATOR)) {
+ try {
+ String natureId = element.getAttribute(ATT_NATUREID);
+ ITeamDecorator decorator = (ITeamDecorator)TeamUIPlugin.createExtension(element, ATT_CLASS);
+ decorators.put(natureId, decorator);
+ } catch(ClassCastException e) {
+ TeamUIPlugin.log(new Status(IStatus.ERROR, TeamUIPlugin.ID, 0, Policy.bind("TeamResourceDecorator.badClassType"), e));
+ } catch(CoreException e) {
+ TeamUIPlugin.log(new Status(IStatus.ERROR, TeamUIPlugin.ID, 0, Policy.bind("TeamResourceDecorator.coreException"), e));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates label change events for the entire subtree rooted
+ * at the given resource.
+ *
+ * @param parent the parent resource to recursively visit
+ * @param events the list to add decorator events to
+ */
+ private void createEventsForSubtree(IResource parent, final ArrayList events) {
+ try {
+ parent.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ events.add(createLabelEvent(resource));
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ //this is never thrown in the above visitor
+ }
+ }
+
+ /**
+ * Returns a new change event to be fired for updates to the given resource.
+ */
+ protected LabelProviderChangedEvent createLabelEvent(IResource resource) {
+ return new LabelProviderChangedEvent(this, resource);
+ }
+
+ /*
+ * Method declared on ILabelDecorator.
+ */
+ public Image decorateImage(Image image, Object object) {
+ IResource resource = getResource(object);
+ if (resource == null) {
+ return image;
+ }
+
+ ITeamDecorator decorator = getDecorator(resource);
+ if(decorator==null) {
+ return image;
+ }
+
+ ImageDescriptor[][] overlays = decorator.getImage(resource);
+
+ if(overlays==null) {
+ return image;
+ }
+
+ ImageDescriptor overlayImage = new OverlayIcon(image.getImageData(), overlays, new Point(16, 16));
+ return overlayImage.createImage();
+ }
+
+ /**
+ * Return a decorator for the given resource
+ *
+ * @param resource the resource to return a decorator for
+ * @return a decorator for the given resource
+ */
+ protected ITeamDecorator getDecorator(IResource resource) {
+ try {
+ String[] natureIds = resource.getProject().getDescription().getNatureIds();
+ for (int i = 0; i < natureIds.length; i++) {
+ if(decorators.containsKey(natureIds[i])) {
+ return (ITeamDecorator)decorators.get(natureIds[i]);
+ }
+ }
+ } catch(CoreException e) {
+ }
+ return null;
+ }
+
+ /*
+ * Method declared on ILabelDecorator.
+ */
+ public String decorateText(String text, Object o) {
+ IResource resource = getResource(o);
+ if (resource == null) {
+ //don't annotate things we don't know about
+ return text;
+ }
+
+ ITeamDecorator decorator = getDecorator(resource);
+ if(decorator==null) {
+ return text;
+ }
+
+ return decorator.getText(text, resource);
+ }
+
+ /*
+ * Method declared on IBaseLabelProvider.
+ */
+ public void dispose() {
+ super.dispose();
+ }
+
+ /**
+ * Returns the resource for the given input object, or
+ * null if there is no resource associated with it.
+ *
+ * @param object the object to find the resource for
+ * @return the resource for the given object, or null
+ */
+ private IResource getResource(Object object) {
+ if (object instanceof IResource) {
+ return (IResource)object;
+ }
+ if (object instanceof IAdaptable) {
+ return (IResource)((IAdaptable)object).getAdapter(IResource.class);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the given object is an outgoing change, and
+ * false otherwise.
+ *
+ * @param object the object to examine for outgoing changes
+ * @return whether the object has an outgoing change
+ */
+ private boolean isOutgoing(Object object) {
+ return false;
+ }
+
+ /**
+ * Process a resource delta. Returns all label provider changed
+ * events that were generated by this delta.
+ *
+ * @param delta the delta to process
+ * @return all label provider changed events
+ */
+ protected LabelProviderChangedEvent[] processDelta(IResourceDelta delta) {
+ final ArrayList events = new ArrayList();
+ try {
+ delta.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ IResource resource = delta.getResource();
+ // skip workspace root
+ if (resource.getType() == IResource.ROOT) {
+ return true;
+ }
+ // don't care about deletions
+ if (delta.getKind() == IResourceDelta.REMOVED) {
+ return false;
+ }
+ // if project association info has changed, need to update whole tree
+ if (resource.getType() == IResource.PROJECT && ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0)) {
+ createEventsForSubtree(resource, events);
+ return false;
+ }
+ // ignore subtrees that aren't associated with a provider
+ ITeamProvider p = TeamPlugin.getManager().getProvider(resource);
+ if (p == null) {
+ return false;
+ }
+ // ignore subtrees that are ignored by team
+ if (!p.hasRemote(resource)) {
+ return false;
+ }
+ // chances are the team outgoing bit needs to be updated
+ // if any child has changed.
+ events.add(createLabelEvent(delta.getResource()));
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ TeamUIPlugin.log(e.getStatus());
+ }
+ // convert event list to array
+ LabelProviderChangedEvent[] result = new LabelProviderChangedEvent[events.size()];
+ events.toArray(result);
+ return result;
+ }
+
+ /**
+ * Post the label events to the UI thread
+ *
+ * @param events the events to post
+ */
+ protected void postLabelEvents(final LabelProviderChangedEvent[] events) {
+ // now post the change events to the UI thread
+ if (events.length > 0 && shell != null && !shell.isDisposed()) {
+ shell.getDisplay().asyncExec(new Runnable() {
+ public void run() {
+ fireLabelUpdates(events);
+ }
+ });
+ }
+ }
+
+ /**
+ * Trigger label updates for the given events
+ *
+ * @param events the events to trigger label updates for
+ */
+ void fireLabelUpdates(final LabelProviderChangedEvent[] events) {
+ for (int i = 0; i < events.length; i++) {
+ fireLabelProviderChanged(events[i]);
+ }
+ }
+
+ /*
+ * Method declared on IResourceChangedListener.
+ */
+ public void resourceChanged(IResourceChangeEvent event) {
+ //first collect the label change events
+ final LabelProviderChangedEvent[] events = processDelta(event.getDelta());
+ postLabelEvents(events);
+ }
+
+ /**
+ * Create a label event for the parents of the resource
+ *
+ * @param resource the resoure to create label events for
+ * @return a list of events
+ */
+ public List createLabelEventForParents(IResource resource) {
+ List events = new ArrayList(5);
+ IResource current = resource;
+ while(current.getType() != IResource.ROOT) {
+ events.add(createLabelEvent(current));
+ current = current.getParent();
+ }
+ return events;
+ }
+
+ /*
+ * Method declared on IResourceStateChangeListener.
+ */
+ public void resourceStateChanged(IResource[] changedResources) {
+ List events = new ArrayList(10);
+ for(int i=0;i<changedResources.length;i++) {
+ events.addAll(createLabelEventForParents(changedResources[i]));
+ }
+ postLabelEvents((LabelProviderChangedEvent[])events.toArray(new LabelProviderChangedEvent[events.size()]));
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/UIConstants.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/UIConstants.java
new file mode 100644
index 000000000..8d6feb502
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/UIConstants.java
@@ -0,0 +1,23 @@
+package org.eclipse.team.internal.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Central location for constants used by the Team user interface.
+ */
+public interface UIConstants {
+ // plugin id
+ public final String PLUGIN_ID = "org.eclipse.team.ui";
+
+ // extension points
+ public final String PT_CONFIGURATION ="configurationWizards";
+ public final String PT_DECORATORS = "decorators";
+
+ // image paths
+ public final String ICON_PATH_FULL = "icons/full/";
+ public final String ICON_PATH_BASIC = "icons/basic/";
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties
new file mode 100644
index 000000000..00fb2bd07
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/messages.properties
@@ -0,0 +1,57 @@
+# String resources for org.eclipse.team.ui
+####################
+
+simpleInternal=Internal error
+exceptionCreatingWizard=Exception creating wizard.
+
+# ui
+
+CheckInAction.checkin=Check In
+CheckInAction.checkingIn=Checking In...
+
+CheckOutAction.checkout=Check Out
+CheckOutAction.checkingOut=Checking Out...
+
+ConfigureProjectAction.configureProject=Configure Project
+
+DeconfigureProjectAction.deconfigureProject=Deconfigure Project
+
+DeleteAction.delete=Delete
+DeleteAction.deleting=Deleting...
+
+ManageAction.manage=Manage
+ManageAction.managing=Managing...
+
+MoveAction.move=Move
+
+UnManageAction.unmanage=Unmanage
+UnManageAction.unmanaging=Unmanaging...
+
+UndoCheckOutAction.undoCheckout=Undo Check Out
+UndoCheckOutAction.undoing=Undoing Check Out...
+
+UpdateAction.updating=Getting...
+UpdateAction.title=Get
+
+# internal.ui
+
+ConfigureProjectWizard.configureProject=Configure Project
+ConfigureProjectWizard.description=Select the type of repository the selected project should be associated with.
+
+ConfigureProjectWizardMainPage.selectRepository=Select a repository type:
+
+ASCIIPreferencePage.ascii=ASCII
+ASCIIPreferencePage.binary=Binary
+ASCIIPreferencePage.description=&File extensions with known content:
+ASCIIPreferencePage.add=&Add...
+ASCIIPreferencePage.remove=&Remove
+ASCIIPreferencePage.enterExtensionShort=Enter File Extension
+ASCIIPreferencePage.enterExtensionLong=Please enter a file extension:
+ASCIIPreferencePage.extensionExistsShort=Extension Already Exists
+ASCIIPreferencePage.extensionExistsLong=The entered extension already exists.
+ASCIIPreferencePage.change=&Change
+ASCIIPreferencePage.extension=Extension
+ASCIIPreferencePage.contents=Contents
+
+TeamResourceDecorator.badClassType=Error creating Team decorator: bad class type
+TeamResourceDecorator.coreException=Error creating Team decorator: core exception
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/IConfigurationWizard.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/IConfigurationWizard.java
new file mode 100644
index 000000000..168015f72
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/IConfigurationWizard.java
@@ -0,0 +1,30 @@
+package org.eclipse.team.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.ui.IWorkbench;
+
+/**
+ * IConfigurationWizard defines the interface that users of the extension
+ * point org.eclipse.team.ui.configurationWizards must implement.
+ */
+public interface IConfigurationWizard extends IWizard {
+ /**
+ * Initializes this creation wizard using the passed workbench and
+ * object selection.
+ * <p>
+ * This method is called after the no argument constructor and
+ * before other methods are called.
+ * </p>
+ *
+ * @param workbench the current workbench
+ * @param project the selected project
+ */
+ void init(IWorkbench workbench, IProject project);
+}
+
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ISharedImages.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ISharedImages.java
new file mode 100644
index 000000000..1b324faa4
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ISharedImages.java
@@ -0,0 +1,21 @@
+package org.eclipse.team.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.resource.ImageDescriptor;
+
+/**
+ * Images that are available for providers to re-use. They include
+ * common overlays and wizard images. A provider can use their own
+ * custom images, these shared images are only available for
+ * convenience.
+ */
+public interface ISharedImages {
+ public final String IMG_DIRTY_OVR = "ovr/dirty_ov.gif";
+ public final String IMG_CHECKEDIN_OVR = "ovr/checkedin_ov.gif";
+ public final String IMG_CHECKEDOUT_OVR = "ovr/checkedout_ov.gif";
+}
+
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ITeamDecorator.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ITeamDecorator.java
new file mode 100644
index 000000000..a93b2e74c
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/ITeamDecorator.java
@@ -0,0 +1,46 @@
+package org.eclipse.team.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+
+/**
+ * The interface that users of the extension point
+ * org.eclipse.team.ui.decorators must implement.
+ *
+ * @see ILabelProvider
+ */
+public interface ITeamDecorator {
+
+ /**
+ * Provider returns the annotated (e.g. including a postfix or
+ * prefix) text to be displayed to the user. The decorator does
+ * not know what type of view the resource is being displayed in.
+ * For example, the provider could decorate text such as:
+ * MyJavaFile.java [1.1]
+ * Main.java [1.2.1.1]
+ *
+ * @param text to be decorated.
+ * @param element the element whose image is being decorated
+ * @return the decorated text label, or <code>null</code> if no
+ * decoration is to be applied
+ */
+ public String getText(String text, IResource resource);
+
+ /**
+ * Returns images that are based on the given image,
+ * but decorated with additional information relating to the state
+ * of the provided element.
+ *
+ * @param element the element whose image is being decorated
+ * @return images to overlay, or <code>null</code> if no decoration
+ * is to be applied.
+ */
+ public ImageDescriptor[][] getImage(IResource resource);
+}
+
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/TeamUIPlugin.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/TeamUIPlugin.java
new file mode 100644
index 000000000..76f2dfc98
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/TeamUIPlugin.java
@@ -0,0 +1,179 @@
+package org.eclipse.team.ui;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Hashtable;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IPluginDescriptor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.team.internal.ui.UIConstants;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * TeamUIPlugin is the plugin for generic, non-provider specific,
+ * team UI functionality in the workbench.
+ */
+public class TeamUIPlugin extends AbstractUIPlugin implements ISharedImages {
+
+ private static TeamUIPlugin instance;
+ public static final String ID = "org.eclipse.team.ui";
+
+ private Hashtable imageDescriptors = new Hashtable(20);
+
+ public final static String ICON_PATH;
+
+ static {
+ if (Display.getCurrent().getIconDepth() > 4) {
+ ICON_PATH = UIConstants.ICON_PATH_FULL;
+ } else {
+ ICON_PATH = UIConstants.ICON_PATH_BASIC;
+ }
+ }
+
+ /**
+ * Creates a new TeamUIPlugin.
+ *
+ * @param descriptor the plugin descriptor
+ */
+ public TeamUIPlugin(IPluginDescriptor descriptor) {
+ super(descriptor);
+ instance = this;
+ }
+ /**
+ * Creates an extension. If the extension plugin has not
+ * been loaded a busy cursor will be activated during the duration of
+ * the load.
+ *
+ * @param element the config element defining the extension
+ * @param classAttribute the name of the attribute carrying the class
+ * @return the extension object
+ */
+ public static Object createExtension(final IConfigurationElement element, final String classAttribute) throws CoreException {
+ // If plugin has been loaded create extension.
+ // Otherwise, show busy cursor then create extension.
+ IPluginDescriptor plugin = element.getDeclaringExtension().getDeclaringPluginDescriptor();
+ if (plugin.isPluginActivated()) {
+ return element.createExecutableExtension(classAttribute);
+ } else {
+ final Object [] ret = new Object[1];
+ final CoreException [] exc = new CoreException[1];
+ BusyIndicator.showWhile(null, new Runnable() {
+ public void run() {
+ try {
+ ret[0] = element.createExecutableExtension(classAttribute);
+ } catch (CoreException e) {
+ exc[0] = e;
+ }
+ }
+ });
+ if (exc[0] != null)
+ throw exc[0];
+ else
+ return ret[0];
+ }
+ }
+ /**
+ * Creates an image and places it in the image registry.
+ *
+ * @param id the identifier for the image
+ * @param baseURL the base URL for the image
+ */
+ protected void createImageDescriptor(String id, URL baseURL) {
+ URL url = null;
+ try {
+ url = new URL(baseURL, UIConstants.ICON_PATH_FULL + id);
+ } catch (MalformedURLException e) {
+ }
+ ImageDescriptor desc = ImageDescriptor.createFromURL(url);
+ imageDescriptors.put(id, desc);
+ }
+ /**
+ * Convenience method to get the currently active workbench page
+ *
+ * @return the active workbench page
+ */
+ public static IWorkbenchPage getActivePage() {
+ return getPlugin().getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ }
+ /**
+ * Return the default instance of the receiver. This represents the runtime plugin.
+ *
+ * @return the singleton plugin instance
+ */
+ public static TeamUIPlugin getPlugin() {
+ return instance;
+ }
+ /**
+ * Returns the image descriptor for the given image ID.
+ * Returns null if there is no such image.
+ *
+ * @param id the identifier for the image to retrieve
+ * @return the image associated with the given ID
+ */
+ public ImageDescriptor getImageDescriptor(String id) {
+ return (ImageDescriptor) imageDescriptors.get(id);
+ }
+ /**
+ * Convenience method to get an image descriptor for an extension
+ *
+ * @param extension the extension declaring the image
+ * @param subdirectoryAndFilename the path to the image
+ * @return the image
+ */
+ public static ImageDescriptor getImageDescriptorFromExtension(IExtension extension, String subdirectoryAndFilename) {
+ IPluginDescriptor pluginDescriptor = extension.getDeclaringPluginDescriptor();
+ URL path = pluginDescriptor.getInstallURL();
+ URL fullPathString = null;
+ try {
+ fullPathString = new URL(path,subdirectoryAndFilename);
+ return ImageDescriptor.createFromURL(fullPathString);
+ } catch (MalformedURLException e) {
+ }
+ return null;
+ }
+ /**
+ * Initializes the table of images used in this plugin.
+ */
+ protected void initializeImages() {
+ URL baseURL = getDescriptor().getInstallURL();
+
+ // View decoration overlays
+ createImageDescriptor(ISharedImages.IMG_DIRTY_OVR, baseURL);
+ createImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR, baseURL);
+ createImageDescriptor(ISharedImages.IMG_CHECKEDOUT_OVR, baseURL);
+ }
+ /**
+ * Initializes the preferences for this plugin if necessary.
+ */
+ protected void initializePreferences() {
+ //IPreferenceStore store = getPreferenceStore();
+ }
+ /**
+ * Convenience method for logging statuses to the plugin log
+ *
+ * @param status the status to log
+ */
+ public static void log(IStatus status) {
+ getPlugin().getLog().log(status);
+ }
+ /**
+ * @see Plugin#startup()
+ */
+ public void startup() throws CoreException {
+ Policy.localize("org.eclipse.team.internal.ui.messages");
+ initializeImages();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckInAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckInAction.java
new file mode 100644
index 000000000..b6b7063c5
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckInAction.java
@@ -0,0 +1,69 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import java.util.Set;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for checking in the selected resources
+ */
+public class CheckInAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("CheckInAction.checkingIn"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ ITeamProvider provider = (ITeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.checkin(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("CheckInAction.checkin"), this.PROGRESS_DIALOG);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() {
+ IResource[] resources = getSelectedResources();
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!provider.isCheckedOut(resource)) return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckOutAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckOutAction.java
new file mode 100644
index 000000000..86227984b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/CheckOutAction.java
@@ -0,0 +1,68 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import java.util.Set;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * CheckOutAction checks the selected resources out from the provider.
+ */
+public class CheckOutAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("CheckOutAction.checkingOut"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ ITeamProvider provider = (ITeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.checkout(providerResources, IResource.DEPTH_ZERO, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("CheckOutAction.checkout"), this.PROGRESS_BUSYCURSOR);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (provider.isCheckedOut(resources[i])) return false;
+ }
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ConfigureProjectAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ConfigureProjectAction.java
new file mode 100644
index 000000000..3bf6fb643
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ConfigureProjectAction.java
@@ -0,0 +1,52 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.ConfigureProjectWizard;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for configuring a project. Configuring involves associating
+ * the project with a Team provider and performing any provider-specific
+ * configuration that is necessary.
+ */
+public class ConfigureProjectAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ IProject project = getSelectedProjects()[0];
+ ConfigureProjectWizard wizard = new ConfigureProjectWizard();
+ wizard.init(null, project);
+ WizardDialog dialog = new WizardDialog(getShell(), wizard);
+ dialog.open();
+ } catch (Exception e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("ConfigureProjectAction.configureProject"), PROGRESS_BUSYCURSOR);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() {
+ IProject[] selectedProjects = getSelectedProjects();
+ if (selectedProjects.length != 1) return false;
+ if (TeamPlugin.getManager().getProvider(selectedProjects[0]) == null) return true;
+ return false;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeconfigureProjectAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeconfigureProjectAction.java
new file mode 100644
index 000000000..0778d67d1
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeconfigureProjectAction.java
@@ -0,0 +1,46 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for deconfiguring a project. Deconfiguring involves removing
+ * associated provider for the project.
+ */
+public class DeconfigureProjectAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ IProject project = getSelectedProjects()[0];
+ TeamPlugin.getManager().removeProvider(project, monitor);
+ } catch (Exception e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("DeconfigureProjectAction.deconfigureProject"), PROGRESS_BUSYCURSOR);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() {
+ IProject[] selectedProjects = getSelectedProjects();
+ if (selectedProjects.length != 1) return false;
+ if (TeamPlugin.getManager().getProvider(selectedProjects[0]) == null) return false;
+ return true;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeleteAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeleteAction.java
new file mode 100644
index 000000000..927b2f60f
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/DeleteAction.java
@@ -0,0 +1,74 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for deleting the selected resources on the provider
+ */
+public class DeleteAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("DeleteAction.deleting"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ ITeamProvider provider = (ITeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.delete(providerResources, subMonitor);
+ for (int i = 0; i < providerResources.length; i++) {
+ providerResources[i].delete(true, monitor);
+ }
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("DeleteAction.delete"), this.PROGRESS_DIALOG);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() {
+ IResource[] resources = getSelectedResources();
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!provider.hasRemote(resources[i])) return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/MoveAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/MoveAction.java
new file mode 100644
index 000000000..b9ef2fe37
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/MoveAction.java
@@ -0,0 +1,41 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for moving the selected resources on the provider
+ */
+public class MoveAction extends TeamAction {
+ /** (Non-javadoc)
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+// try {
+// IResource resource = getSelectedResources()[0];
+// ITeamProvider provider = TeamPlugin.getPlugin().getProvider(resource.getProject());
+// // add move here
+// } catch (TeamProviderException e) {
+// throw new InvocationTargetException(e);
+// }
+ }
+ }, Policy.bind("MoveAction.move"), this.PROGRESS_DIALOG);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() {
+ return false;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/TeamAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/TeamAction.java
new file mode 100644
index 000000000..2cdace291
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/TeamAction.java
@@ -0,0 +1,298 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.actions.ActionDelegate;
+
+/**
+ * The abstract superclass of all Team actions. This class contains some convenience
+ * methods for getting selected objects and mapping selected objects to their
+ * providers.
+ *
+ * Team providers may subclass this class when creating their actions.
+ * Team providers may also instantiate or subclass any of the
+ * subclasses of TeamAction provided in this package.
+ */
+public abstract class TeamAction extends ActionDelegate implements IObjectActionDelegate {
+ // The current selection
+ protected IStructuredSelection selection;
+
+ // The shell, required for the progress dialog
+ protected Shell shell;
+
+ // Constants for determining the type of progress. Subclasses may
+ // pass one of these values to the run method.
+ public final static int PROGRESS_DIALOG = 1;
+ public final static int PROGRESS_BUSYCURSOR = 2;
+
+ /**
+ * Returns the selected projects.
+ *
+ * @return the selected projects
+ */
+ protected IProject[] getSelectedProjects() {
+ ArrayList projects = null;
+ if (!selection.isEmpty()) {
+ projects = new ArrayList();
+ Iterator elements = ((IStructuredSelection) selection).iterator();
+ while (elements.hasNext()) {
+ Object next = elements.next();
+ if (next instanceof IProject) {
+ projects.add(next);
+ continue;
+ }
+ if (next instanceof IAdaptable) {
+ IAdaptable a = (IAdaptable) next;
+ Object adapter = a.getAdapter(IResource.class);
+ if (adapter instanceof IProject) {
+ projects.add(adapter);
+ continue;
+ }
+ }
+ }
+ }
+ if (projects != null && !projects.isEmpty()) {
+ IProject[] result = new IProject[projects.size()];
+ projects.toArray(result);
+ return result;
+ }
+ return new IProject[0];
+ }
+ /**
+ * Returns the selected resources.
+ *
+ * @return the selected resources
+ */
+ protected IResource[] getSelectedResources() {
+ ArrayList resources = null;
+ if (!selection.isEmpty()) {
+ resources = new ArrayList();
+ Iterator elements = ((IStructuredSelection) selection).iterator();
+ while (elements.hasNext()) {
+ Object next = elements.next();
+ if (next instanceof IResource) {
+ resources.add(next);
+ continue;
+ }
+ if (next instanceof IAdaptable) {
+ IAdaptable a = (IAdaptable) next;
+ Object adapter = a.getAdapter(IResource.class);
+ if (adapter instanceof IResource) {
+ resources.add(adapter);
+ continue;
+ }
+ }
+ }
+ }
+ if (resources != null && !resources.isEmpty()) {
+ IResource[] result = new IResource[resources.size()];
+ resources.toArray(result);
+ return result;
+ }
+ return new IResource[0];
+ }
+
+ /**
+ * Convenience method for getting the current shell.
+ *
+ * @return the shell
+ */
+ protected Shell getShell() {
+ if (shell != null) {
+ return shell;
+ } else {
+ return TeamUIPlugin
+ .getPlugin()
+ .getWorkbench()
+ .getActiveWorkbenchWindow()
+ .getShell();
+ }
+ }
+ /**
+ * Returns a status object for the given exception.
+ *
+ * @param t the throwable to get a status for
+ * @return a status for the given throwable
+ */
+ protected IStatus getStatus(Throwable t) {
+ if (t instanceof CoreException) {
+ return ((CoreException) t).getStatus();
+ }
+ return new Status(
+ IStatus.ERROR,
+ TeamUIPlugin.ID,
+ 1,
+ Policy.bind("simpleInternal"),
+ t);
+ }
+ /**
+ * Convenience method for running an operation with progress and
+ * error feedback.
+ *
+ * @param runnable the runnable which executes the operation
+ * @param problemMessage the message to display in the case of errors
+ * @param progressKind one of PROGRESS_BUSYCURSOR or PROGRESS_DIALOG
+ */
+ final protected void run(final IRunnableWithProgress runnable, final String problemMessage, int progressKind) {
+ final IStatus[] errors = new IStatus[] {null};
+ switch (progressKind) {
+ case PROGRESS_BUSYCURSOR :
+ BusyIndicator.showWhile(Display.getCurrent(), new Runnable() {
+ public void run() {
+ try {
+ runnable.run(new NullProgressMonitor());
+ } catch (InvocationTargetException e) {
+ errors[0] = getStatusFromException(e, problemMessage);
+ } catch (InterruptedException e) {
+ errors[0] = null;
+ }
+ }
+ });
+ break;
+ default :
+ case PROGRESS_DIALOG :
+ try {
+ new ProgressMonitorDialog(getShell()).run(false, false, runnable);
+ } catch (InvocationTargetException e) {
+ errors[0] = getStatusFromException(e, problemMessage);
+ } catch (InterruptedException e) {
+ errors[0] = null;
+ }
+ break;
+ }
+ if (errors[0] != null) {
+ String msg = problemMessage;
+ ErrorDialog.openError(getShell(), msg, null, errors[0]);
+ TeamUIPlugin.log(errors[0]);
+ }
+ }
+ /**
+ * Convenience method for converting an exception into the appropriate status object.
+ *
+ * @param e the exception to get a status for
+ * @param msg the message to include in the status
+ * @return a status for the given exception
+ */
+ private IStatus getStatusFromException(InvocationTargetException e, String msg) {
+ Throwable t = e.getTargetException();
+ IStatus errors = null;
+ if (t instanceof TeamException) {
+ errors = ((TeamException) t).getStatus();
+ } else {
+ errors = new Status(IStatus.ERROR, TeamUIPlugin.ID, 1, msg, t);
+ }
+ return errors;
+ }
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ if (selection instanceof IStructuredSelection) {
+ this.selection = (IStructuredSelection) selection;
+ try {
+ action.setEnabled(isEnabled());
+ } catch (TeamException e) {
+ action.setEnabled(false);
+ }
+ }
+ }
+ /*
+ * Method declared on IObjectActionDelegate.
+ */
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ this.shell = targetPart.getSite().getShell();
+ }
+ /**
+ * Shows the given errors to the user.
+ *
+ * @param status the status containing the error
+ * @param title the title of the error dialog
+ * @param message the message for the error dialog
+ * @param shell the shell to open the error dialog in
+ */
+ protected void showError(IStatus status, String title, String message, Shell shell) {
+ if (!status.isOK()) {
+ IStatus toShow = status;
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ if (children.length == 1) {
+ toShow = children[0];
+ }
+ }
+ if (title == null)
+ title = status.getMessage();
+ ErrorDialog.openError(shell, title, message, toShow);
+ TeamUIPlugin.log(toShow);
+ }
+ }
+ /**
+ * Shows the given errors to the user.
+ *
+ * @param status the status containing the error to show
+ */
+ protected void showError(IStatus status) {
+ showError(status, null, null, getShell());
+ }
+ /**
+ * Concrete action enablement code.
+ * Subclasses must implement.
+ *
+ * @return whether the action is enabled
+ * @throws TeamException if an error occurs during enablement detection
+ */
+ abstract protected boolean isEnabled() throws TeamException;
+
+ /**
+ * Convenience method that maps the selected resources to their providers.
+ * The returned Hashtable has keys which are ITeamProviders, and values
+ * which are Lists of IResources that are shared with that provider.
+ *
+ * @return a hashtable mapping providers to their selected resources
+ */
+ protected Hashtable getProviderMapping() {
+ IResource[] resources = getSelectedResources();
+ Hashtable result = new Hashtable();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = TeamPlugin.getManager().getProvider(resources[i].getProject());
+ List list = (List)result.get(provider);
+ if (list == null) {
+ list = new ArrayList();
+ result.put(provider, list);
+ }
+ list.add(resources[i]);
+ }
+ return result;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ToggleNavigatorDecorations.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ToggleNavigatorDecorations.java
new file mode 100644
index 000000000..886d463e8
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/ToggleNavigatorDecorations.java
@@ -0,0 +1,62 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.internal.ui.TeamResourceDecorator;
+import org.eclipse.ui.IViewActionDelegate;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.views.navigator.ResourceNavigator;
+
+/**
+ * This action adds the team decorator to the navigator
+ */
+public class ToggleNavigatorDecorations implements IViewActionDelegate {
+
+ private IViewPart part;
+
+ /*
+ * Method declared on IViewActionDelegate.
+ */
+ public void init(IViewPart part) {
+ this.part = part;
+ if (part instanceof ResourceNavigator) {
+ final ResourceNavigator navigator = (ResourceNavigator)part;
+ final Shell shell = part.getSite().getShell();
+ BusyIndicator.showWhile(shell.getDisplay(), new Runnable() {
+ public void run() {
+ navigator.setLabelDecorator(new TeamResourceDecorator(shell));
+ }
+ });
+ }
+ }
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ if (part instanceof ResourceNavigator) {
+ final ResourceNavigator navigator = (ResourceNavigator)part;
+ if (action.isChecked()) {
+ final Shell shell = part.getSite().getShell();
+ BusyIndicator.showWhile(shell.getDisplay(), new Runnable() {
+ public void run() {
+ navigator.setLabelDecorator(new TeamResourceDecorator(shell));
+ }
+ });
+ } else {
+ navigator.setLabelDecorator(null);
+ }
+ }
+ }
+ /*
+ * Method declared on IActionDelegate
+ */
+ public void selectionChanged(IAction action, ISelection selection) {
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UndoCheckOutAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UndoCheckOutAction.java
new file mode 100644
index 000000000..f0667893b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UndoCheckOutAction.java
@@ -0,0 +1,69 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for undoing a previous checkout operation.
+ */
+public class UndoCheckOutAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("UndoCheckOutAction.undoing"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ ITeamProvider provider = (ITeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.uncheckout(providerResources, IResource.DEPTH_ZERO, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("UndoCheckOutAction.undoCheckout"), this.PROGRESS_BUSYCURSOR);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!provider.hasRemote(resources[i])) return false;
+ if (!provider.isCheckedOut(resources[i])) return false;
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UpdateAction.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UpdateAction.java
new file mode 100644
index 000000000..bb577491c
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/actions/UpdateAction.java
@@ -0,0 +1,68 @@
+package org.eclipse.team.ui.actions;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.ITeamManager;
+import org.eclipse.team.core.ITeamProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.TeamPlugin;
+import org.eclipse.team.internal.ui.Policy;
+
+/**
+ * Action for getting the contents of the selected resources
+ */
+public class UpdateAction extends TeamAction {
+ /*
+ * Method declared on IActionDelegate.
+ */
+ public void run(IAction action) {
+ run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ Hashtable table = getProviderMapping();
+ Set keySet = table.keySet();
+ monitor.beginTask("", keySet.size() * 1000);
+ monitor.setTaskName(Policy.bind("UpdateAction.updating"));
+ Iterator iterator = keySet.iterator();
+ while (iterator.hasNext()) {
+ IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1000);
+ ITeamProvider provider = (ITeamProvider)iterator.next();
+ List list = (List)table.get(provider);
+ IResource[] providerResources = (IResource[])list.toArray(new IResource[list.size()]);
+ provider.get(providerResources, IResource.DEPTH_INFINITE, subMonitor);
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, Policy.bind("UpdateAction.title"), PROGRESS_DIALOG);
+ }
+ /**
+ * @see TeamAction#isEnabled()
+ */
+ protected boolean isEnabled() throws TeamException {
+ IResource[] resources = getSelectedResources();
+ if (resources.length == 0) return false;
+ ITeamManager manager = TeamPlugin.getManager();
+ for (int i = 0; i < resources.length; i++) {
+ ITeamProvider provider = manager.getProvider(resources[i].getProject());
+ if (provider == null) return false;
+ if (!provider.hasRemote(resources[i])) return false;
+ }
+ return true;
+ }
+} \ No newline at end of file

Back to the top