Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjames2001-11-22 16:41:31 -0500
committerjames2001-11-22 16:41:31 -0500
commit79487a49b38b74f17f8ca3f11f4d1ee42676fc3a (patch)
treef9c15b06ef3e8309ea931b5a5e164a335a557f5c
parent44bef0be945bba181d0dc3fd9577971f1e21e489 (diff)
downloadeclipse.platform.team-79487a49b38b74f17f8ca3f11f4d1ee42676fc3a.tar.gz
eclipse.platform.team-79487a49b38b74f17f8ca3f11f4d1ee42676fc3a.tar.xz
eclipse.platform.team-79487a49b38b74f17f8ca3f11f4d1ee42676fc3a.zip
InitialV2_0_0
-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',