001package org.nasdanika.models.gitlab.util;
002
003import java.util.List;
004import java.util.function.BiConsumer;
005
006import org.eclipse.emf.common.util.EMap;
007import org.gitlab4j.api.Constants.AutoDevopsDeployStrategy;
008import org.gitlab4j.api.Constants.BuildGitStrategy;
009import org.gitlab4j.api.Constants.SquashOption;
010import org.gitlab4j.api.GitLabApi;
011import org.gitlab4j.api.GitLabApiException;
012import org.gitlab4j.api.GroupApi;
013import org.gitlab4j.api.Pager;
014import org.gitlab4j.api.ProjectApi;
015import org.gitlab4j.api.ProjectLicense;
016import org.gitlab4j.api.RepositoryApi;
017import org.gitlab4j.api.RepositoryFileApi;
018import org.gitlab4j.api.models.AbstractUser;
019import org.gitlab4j.api.models.Branch;
020import org.gitlab4j.api.models.Commit;
021import org.gitlab4j.api.models.Contributor;
022import org.gitlab4j.api.models.CustomAttribute;
023import org.gitlab4j.api.models.Group.Statistics;
024import org.gitlab4j.api.models.GroupFilter;
025import org.gitlab4j.api.models.Owner;
026import org.gitlab4j.api.models.Permissions;
027import org.gitlab4j.api.models.Project;
028import org.gitlab4j.api.models.ProjectAccess;
029import org.gitlab4j.api.models.ProjectSharedGroup;
030import org.gitlab4j.api.models.ProjectStatistics;
031import org.gitlab4j.api.models.TreeItem;
032import org.gitlab4j.api.models.Visibility;
033import org.nasdanika.common.ProgressMonitor;
034import org.nasdanika.common.Status;
035import org.nasdanika.models.gitlab.GitLabFactory;
036import org.nasdanika.models.gitlab.MergeMethod;
037
038/**
039 * Loads GitLab model using {@link GitLabApi}.
040 * Provides low-level loading capabilities at a single element.
041 *  
042 */
043public class Loader {
044        
045        public static final String ROOT_PATH = "/";
046        private GitLabApi gitLabApi;
047        private GitLabFactory factory = GitLabFactory.eINSTANCE;
048        private int pageSize = 20;
049        
050        public int getPageSize() {
051                return pageSize;
052        }
053        
054        /**
055         * Page size for retrieving groups.
056         * @param groupsPageSize
057         */
058        public void setPageSize(int pageSize) {
059                this.pageSize = pageSize;
060        }
061        
062        /**
063         * Access to the API for configuration. 
064         * @return
065         */
066        public GitLabApi getGitLabApi() {
067                return gitLabApi;
068        }
069        
070        public Loader(GitLabApi gitLabApi) {
071                this.gitLabApi = gitLabApi;
072        }       
073        
074        public GitLabFactory getFactory() {
075                return factory;
076        }
077        
078        public void setFactory(GitLabFactory factory) {
079                this.factory = factory;
080        }
081        
082        /**
083         * This implementation returns a new instance of {@link GroupFilter}.
084         * Override to customize.
085         * @return
086         */
087        protected GroupFilter getGroupFilter() {
088                return new GroupFilter();
089        }
090        
091        // ---
092
093        /**
094         * @param groupConsumer Group bi-consumer which may trigger downstream operations such as loading sub-groups and projects. It takes a group and a progress monitor for group-related downstream operations. 
095         * @param progressMonitor
096         */
097        public void loadTopLevelGroups(BiConsumer<org.nasdanika.models.gitlab.Group, ProgressMonitor> groupConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
098                try (ProgressMonitor groupsMonitor = progressMonitor.split("Loading top-level groups", 1)) {
099                        GroupApi groupApi = gitLabApi.getGroupApi();
100                        Pager<org.gitlab4j.api.models.Group> groupPager = groupApi.getGroups(getGroupFilter().withTopLevelOnly(true), getPageSize());
101                        loadGroupPager(groupApi, groupPager, groupConsumer, groupsMonitor);
102                }
103        }
104        
105        /**
106         * @param groupConsumer Group bi-consumer which may trigger downstream operations such as loading sub-groups and projects. It takes a group and a progress monitor for group-related downstream operations. 
107         * @param progressMonitor
108         */
109        public void loadSubGroups(long groupId, BiConsumer<org.nasdanika.models.gitlab.Group, ProgressMonitor> groupConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
110                try (ProgressMonitor groupsMonitor = progressMonitor.split("Loading sub-groups of " + groupId, 1)) {
111                        GroupApi groupApi = gitLabApi.getGroupApi();
112                        Pager<org.gitlab4j.api.models.Group> groupPager = groupApi.getDescendantGroups(groupId, getGroupFilter(), getPageSize());
113                        loadGroupPager(groupApi, groupPager, groupConsumer, groupsMonitor);
114                }
115        }
116
117        protected void loadGroupPager(
118                        GroupApi groupApi, 
119                        Pager<org.gitlab4j.api.models.Group> groupPager,
120                        BiConsumer<org.nasdanika.models.gitlab.Group, ProgressMonitor> groupConsumer,
121                        ProgressMonitor groupsMonitor) throws GitLabApiException {
122                int pageNum = 0;
123                while (groupPager.hasNext()) {
124                        ++pageNum;
125                        double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
126                        try (ProgressMonitor groupPageMonitor = groupsMonitor.split("Group page " + pageNum, monitorSize)) {
127                                List<org.gitlab4j.api.models.Group> groups = groupPager.next();
128                                try (ProgressMonitor scaledGroupsMonitor = groupPageMonitor.scale(groups.size()*2 + 1)) {
129                                        scaledGroupsMonitor.worked(Status.INFO, 1, "Retrieved " + groups.size() + " groups");
130                                        for (org.gitlab4j.api.models.Group group: groups) {
131                                                try (ProgressMonitor groupMonitor = scaledGroupsMonitor.split("Loading group " + group.getName() + " " + group.getId(), 2, group)) {
132                                                        org.nasdanika.models.gitlab.Group modelGroup = loadGroup(group, groupApi, groupMonitor.split("Loading group data and members", 1));
133                                                        groupConsumer.accept(modelGroup, groupMonitor.split("Consuming group", 1));
134                                                }
135                                        }
136                                }
137                        }
138                }
139        }
140                
141        protected org.nasdanika.models.gitlab.Group createGroup(org.gitlab4j.api.models.Group group, ProgressMonitor progressMonitor) {         
142                return getFactory().createGroup();
143        }
144        
145        protected org.nasdanika.models.gitlab.Group loadGroup(
146                        org.gitlab4j.api.models.Group group, 
147                        GroupApi groupApi,                      
148                        ProgressMonitor progressMonitor) throws GitLabApiException {
149                
150                org.nasdanika.models.gitlab.Group modelGroup = createGroup(group, progressMonitor); 
151                modelGroup.setAvatarUrl(group.getAvatarUrl());
152                modelGroup.setCreatedAt(group.getCreatedAt());
153                modelGroup.setDescription(group.getDescription());
154                modelGroup.setFullName(group.getFullName());
155                modelGroup.setFullPath(group.getFullPath());
156                modelGroup.setId(group.getId());
157                Statistics stats = group.getStatistics();
158                if (stats != null) {
159                        modelGroup.setJobArtifactsSize(stats.getJobArtifactsSize());
160                        modelGroup.setLfsObjectsSize(stats.getLfsObjectsSize());
161                        modelGroup.setRepositorySize(stats.getRepositorySize());
162                        modelGroup.setStorageSize(stats.getStorageSize());
163                }
164                modelGroup.setLfsEnabled(group.getLfsEnabled());
165                modelGroup.setName(group.getName());
166                modelGroup.setPath(group.getPath());
167                modelGroup.setVisibility(org.nasdanika.models.gitlab.Visibility.get(group.getVisibility().ordinal()));
168                modelGroup.setWebUrl(group.getWebUrl());
169                
170                return modelGroup;
171        }
172        
173        public void loadGroupMembers(long groupId, BiConsumer<org.nasdanika.models.gitlab.Member, ProgressMonitor> memberConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
174                try (ProgressMonitor membersMonitor = progressMonitor.split("Loading group members of " + groupId, 1)) {
175                        GroupApi groupApi = gitLabApi.getGroupApi();
176                        Pager<org.gitlab4j.api.models.Member> memberPager = groupApi.getMembers(groupId, getPageSize());
177                        int pageNum = 0;
178                        while (memberPager.hasNext()) {
179                                ++pageNum;
180                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
181                                try (ProgressMonitor memberPageMonitor = membersMonitor.split("Group members page " + pageNum, monitorSize)) {
182                                        List<org.gitlab4j.api.models.Member> members = memberPager.next();
183                                        try (ProgressMonitor scaledMembersMonitor = memberPageMonitor.scale(members.size() * 2 + 1)) {
184                                                scaledMembersMonitor.worked(Status.INFO, 1, "Retrieved " + members.size() + " members");
185                                                for (org.gitlab4j.api.models.Member member: members) {
186                                                        try (ProgressMonitor memberMonitor = scaledMembersMonitor.split("Loading member " + member.getName() + " " + member.getId(), 2, member)) {
187                                                                org.nasdanika.models.gitlab.Member modelMember = loadMember(member, memberMonitor.split("Loading member data", 1));
188                                                                memberConsumer.accept(modelMember, memberMonitor.split("Consuming member", 1));
189                                                        }
190                                                }
191                                        }
192                                }
193                        }
194                }
195        }
196
197        protected void populateAbstractUser(AbstractUser<?> apiUser, org.nasdanika.models.gitlab.AbstractUser user) {
198                user.setAvatarUrl(apiUser.getAvatarUrl());
199                user.setCreatedAt(apiUser.getCreatedAt());
200                user.setEMail(apiUser.getEmail());
201                user.setId(apiUser.getId());
202                user.setName(apiUser.getName());
203                user.setState(apiUser.getState());
204                user.setUserName(apiUser.getUsername());
205                user.setWebUrl(apiUser.getWebUrl());
206        }
207        
208        /**
209         * 
210         * @param member
211         * @param userProvider Provides a user instance to reference by the member
212         * @param progressMonitor
213         * @return
214         */
215        protected org.nasdanika.models.gitlab.Member loadMember(org.gitlab4j.api.models.Member member, ProgressMonitor progressMonitor) {
216                org.nasdanika.models.gitlab.Member modelMember = getFactory().createMember();
217                populateAbstractUser(member, modelMember);
218                org.gitlab4j.api.models.AccessLevel accessLevel = member.getAccessLevel();
219                if (accessLevel != null) {
220                        modelMember.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(accessLevel.value));
221                }
222                modelMember.setExpiresAt(member.getExpiresAt());                
223                return modelMember;
224        }       
225        
226        public void loadProjects(long groupId, BiConsumer<org.nasdanika.models.gitlab.Project, ProgressMonitor> projectConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
227                try (ProgressMonitor projectsMonitor = progressMonitor.split("Loading projects of " + groupId, 1)) {
228                        GroupApi groupApi = gitLabApi.getGroupApi();
229                        Pager<org.gitlab4j.api.models.Project> projectPager = groupApi.getProjects(groupId, getPageSize());
230                        int pageNum = 0;
231                        while (projectPager.hasNext()) {
232                                ++pageNum;
233                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
234                                try (ProgressMonitor projectPageMonitor = projectsMonitor.split("Project page " + pageNum, monitorSize)) {
235                                        List<org.gitlab4j.api.models.Project> projects = projectPager.next();
236                                        try (ProgressMonitor scaledProjectsMonitor = projectPageMonitor.scale(projects.size() * 2 + 1)) {
237                                                scaledProjectsMonitor.worked(Status.INFO, 1, "Retrieved " + projects.size() + " projects");
238                                                for (org.gitlab4j.api.models.Project project: projects) {
239                                                        try (ProgressMonitor projectMonitor = scaledProjectsMonitor.split("Loading project " + project.getName() + " " + project.getId(), 2, project)) {
240                                                                org.nasdanika.models.gitlab.Project modelProject = loadProject(project, projectMonitor.split("Loading project data", 1));
241                                                                projectConsumer.accept(modelProject, projectMonitor.split("Consuming project", 1));
242                                                        }
243                                                }
244                                        }
245                                }
246                        }
247                }
248        }
249        
250        /**
251         * Creates a new instance of model project. Called by loadProject(). 
252         * This implementation calls getFactory().createProject(). Override to customize creation. 
253         * E.g. create a subclass of Project, load a project from a prototypes with some information pre-filled, ...
254         * @param project
255         * @param groupProvider
256         * @param projectProvider
257         * @param progressMonitor
258         * @return
259         */
260        protected org.nasdanika.models.gitlab.Project createProject(org.gitlab4j.api.models.Project project, ProgressMonitor progressMonitor) {
261                return getFactory().createProject();
262        }       
263        
264        protected org.nasdanika.models.gitlab.Owner createOwner(Owner owner, ProgressMonitor progressMonitor) {
265                return getFactory().createOwner();
266        }
267        
268        protected org.nasdanika.models.gitlab.Branch createBranch(Branch branch, ProgressMonitor progressMonitor) {
269                return getFactory().createBranch();
270        }
271        
272        protected org.nasdanika.models.gitlab.Contributor createContributor(Contributor contributor, ProgressMonitor progressMonitor) {
273                return getFactory().createContributor();
274        }
275        
276        protected org.nasdanika.models.gitlab.ProjectLicense loadProjectLicense(ProjectLicense apiLicense, ProgressMonitor progressMonitor) {
277                org.nasdanika.models.gitlab.ProjectLicense modelLicense = getFactory().createProjectLicense();
278                modelLicense.setHtmlUrl(apiLicense.getHtmlUrl());
279                modelLicense.setKey(apiLicense.getKey());
280                modelLicense.setName(apiLicense.getName());
281                modelLicense.setNickname(apiLicense.getNickname());
282                modelLicense.setSourceUrl(apiLicense.getSourceUrl());
283                return modelLicense;
284        }
285        
286        protected org.nasdanika.models.gitlab.Project loadProject(org.gitlab4j.api.models.Project project, ProgressMonitor progressMonitor) throws GitLabApiException {
287                                
288                org.nasdanika.models.gitlab.Project modelProject = createProject(project, progressMonitor);
289                
290                modelProject.setId(project.getId());
291                modelProject.setApprovalsBeforeMerge(project.getApprovalsBeforeMerge());
292                modelProject.setArchived(project.getArchived());
293                modelProject.setAvatarUrl(project.getAvatarUrl());
294                modelProject.setContainerRegistryEnabled(project.getContainerRegistryEnabled());
295                modelProject.setCreatedAt(project.getCreatedAt());
296                modelProject.setCreatorId(project.getCreatorId());
297                modelProject.setDefaultBranch(project.getDefaultBranch());
298                modelProject.setDescription(project.getDescription());          
299                modelProject.setForksCount(project.getForksCount());            
300
301                Project forkedFromProject = project.getForkedFromProject();
302                if (forkedFromProject != null) {
303                        modelProject.setForkedFromId(forkedFromProject.getId());
304                }
305                
306                modelProject.setHttpUrlToRepo(project.getHttpUrlToRepo());              
307                modelProject.setIsPublic(project.getPublic());          
308                modelProject.setIssuesEnabled(project.getIssuesEnabled());              
309                modelProject.setJobsEnabled(project.getJobsEnabled());          
310                modelProject.setLastsActivityAt(project.getLastActivityAt());           
311                modelProject.setLfsEnabled(project.getLfsEnabled());            
312                modelProject.setMergeMethod(MergeMethod.get(project.getMergeMethod().ordinal()));               
313                modelProject.setMergeRequestsEnabled(project.getMergeRequestsEnabled());                
314                modelProject.setName(project.getName());                
315                modelProject.setNameWithNamespace(project.getNameWithNamespace());              
316                modelProject.setOnlyAllowMergeIfAllDiscussionsAreResolved(project.getOnlyAllowMergeIfAllDiscussionsAreResolved());              
317                modelProject.setOpenIssuesCount(project.getOpenIssuesCount());          
318                
319                Owner owner = project.getOwner();
320                if (owner != null) {
321                        org.nasdanika.models.gitlab.Owner modelOwner = createOwner(owner, progressMonitor);
322                        populateAbstractUser(owner, modelOwner);
323                        modelProject.setOwner(modelOwner);
324                }
325                
326                modelProject.setPath(project.getPath());
327                modelProject.setPathWithNamespace(project.getPathWithNamespace());
328                
329                Permissions permissions = project.getPermissions();
330                if (permissions != null) {
331                        ProjectAccess groupAccess = permissions.getGroupAccess();
332                        if (groupAccess != null) {
333                                org.nasdanika.models.gitlab.ProjectAccess modelGroupAccess = getFactory().createProjectAccess();
334                                modelGroupAccess.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(groupAccess.getAccessLevel().value));
335                                modelProject.setGroupAccess(modelGroupAccess);
336                        }
337                        ProjectAccess projectAccess = permissions.getProjectAccess();
338                        if (projectAccess != null) {
339                                org.nasdanika.models.gitlab.ProjectAccess modelProjectAccess = getFactory().createProjectAccess();
340                                modelProjectAccess.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(projectAccess.getAccessLevel().value));
341                                modelProject.setGroupAccess(modelProjectAccess);
342                        }
343                }
344                
345                modelProject.setPublicJobs(project.getPublicJobs());
346                modelProject.setRepositoryStorage(project.getRepositoryStorage());
347                modelProject.setRequestAccessEnabled(project.getRequestAccessEnabled());
348                modelProject.setRunnersToken(project.getRunnersToken());
349                modelProject.setSharedRunnersEnabled(project.getSharedRunnersEnabled());
350                
351                List<ProjectSharedGroup> sharedGroups = project.getSharedWithGroups();
352                if (sharedGroups != null) {
353                        for (ProjectSharedGroup sg: sharedGroups) {
354                                org.nasdanika.models.gitlab.ProjectSharedGroup psg = getFactory().createProjectSharedGroup();
355                                org.gitlab4j.api.models.AccessLevel accessLevel = sg.getGroupAccessLevel();
356                                if (accessLevel != null) {
357                                        psg.setAccessLevel(org.nasdanika.models.gitlab.AccessLevel.get(accessLevel.value));
358                                }
359                                psg.setFullPath(sg.getGroupFullPath());
360                                psg.setId(sg.getGroupId());
361                                psg.setName(sg.getGroupName());
362                        }
363                }
364                                
365                modelProject.setSnippetsEnabled(project.getSnippetsEnabled());
366                modelProject.setSshUrlToRepo(project.getSshUrlToRepo());
367                modelProject.setStarCount(project.getStarCount());
368
369                List<String> tags = project.getTagList();
370                if (tags != null) {
371                        modelProject.getTags().addAll(tags);
372                }
373                
374                modelProject.setVisibilityLevel(project.getVisibilityLevel());
375                Visibility projectVisibility = project.getVisibility();
376                if (projectVisibility != null) {
377                        modelProject.setVisibility(org.nasdanika.models.gitlab.Visibility.get(projectVisibility.ordinal()));
378                }
379                modelProject.setWallEnabled(project.getWallEnabled());
380                modelProject.setWebUrl(project.getWebUrl());
381                modelProject.setWikiEnabled(project.getWikiEnabled());
382                modelProject.setPrintingMergeRequestLinkEnabled(project.getPrintingMergeRequestLinkEnabled());
383                modelProject.setResolveOutdatedDiffDiscussions(project.getResolveOutdatedDiffDiscussions());
384                
385                ProjectStatistics projectStatistics = project.getStatistics();
386                if (projectStatistics != null) {
387                        org.nasdanika.models.gitlab.ProjectStatistics modelProjectStatistics = getFactory().createProjectStatistics();
388                        modelProjectStatistics.setCommitCount(projectStatistics.getCommitCount());
389                        modelProjectStatistics.setJobArtifactsSize(projectStatistics.getJobArtifactsSize());
390                        modelProjectStatistics.setLfsObjectsSize(projectStatistics.getLfsObjectsSize());
391                        modelProjectStatistics.setPackagesSize(projectStatistics.getPackagesSize());
392                        modelProjectStatistics.setRepositorySize(projectStatistics.getRepositorySize());
393                        modelProjectStatistics.setStorageSize(projectStatistics.getStorageSize());
394                        modelProjectStatistics.setWikiSize(projectStatistics.getWikiSize());
395                        
396                        modelProject.setStatistics(modelProjectStatistics);
397                }
398                
399                modelProject.setInitializeWithReadme(null);
400                modelProject.setPackagesEnabled(null);
401                modelProject.setEmptyRepo(null);
402                modelProject.setLicenseUrl(null);
403                
404                ProjectLicense apiLicense = project.getLicense();
405                if (apiLicense != null) {
406                        modelProject.setLicense(loadProjectLicense(apiLicense, progressMonitor));
407                }
408
409                List<CustomAttribute> apiCustomAttributes = project.getCustomAttributes();
410                if (apiCustomAttributes != null) {
411                        EMap<String, String> modelCustomAttributes = modelProject.getCustomAttributes();
412                        apiCustomAttributes.forEach(ca -> modelCustomAttributes.put(ca.getKey(), ca.getValue()));
413                }
414                
415                modelProject.setBuildCoverageRegex(project.getBuildCoverageRegex());
416                
417                BuildGitStrategy buildGitStrategy = project.getBuildGitStrategy();
418                if (buildGitStrategy != null) {
419                        modelProject.setBuildGitStrategy(org.nasdanika.models.gitlab.BuildGitStrategy.get(buildGitStrategy.ordinal()));
420                }
421                
422                modelProject.setReadmeUrl(project.getReadmeUrl());
423                modelProject.setCanCreateMergeRequestIn(project.getCanCreateMergeRequestIn());
424                
425                org.gitlab4j.api.models.ImportStatus.Status importStatus = project.getImportStatus();
426                if (importStatus != null) {
427                        modelProject.setImportStatus(org.nasdanika.models.gitlab.Status.get(importStatus.ordinal()));
428                }
429                modelProject.setCiDefaultGitDepth(project.getCiDefaultGitDepth());
430                modelProject.setCiForwardDeploymentEnabled(project.getCiForwardDeploymentEnabled());
431                modelProject.setCiConfigPath(project.getCiConfigPath());
432                modelProject.setRemoveSourceBranchAfterMerge(project.getRemoveSourceBranchAfterMerge());
433                modelProject.setAutoDevopsEnabled(project.getAutoDevopsEnabled());
434                
435                AutoDevopsDeployStrategy autoDevopsDeployStrategy = project.getAutoDevopsDeployStrategy();
436                if (autoDevopsDeployStrategy != null) {
437                        modelProject.setAutoDevopsDeployStrategy(org.nasdanika.models.gitlab.AutoDevopsDeployStrategy.get(autoDevopsDeployStrategy.ordinal()));
438                }
439                
440                modelProject.setAutocloseReferencedIssues(project.getAutocloseReferencedIssues());
441                modelProject.setEmailsDisabled(project.getEmailsDisabled());
442                modelProject.setSuggestionCommitMessage(project.getSuggestionCommitMessage());
443                
444                SquashOption squashOption = project.getSquashOption();
445                if (squashOption != null) {
446                        modelProject.setSquashOption(org.nasdanika.models.gitlab.SquashOption.get(squashOption.ordinal()));
447                }               
448                                        
449                return modelProject;
450        }       
451                        
452        public void loadProjectMembers(long projectId, BiConsumer<org.nasdanika.models.gitlab.Member, ProgressMonitor> memberConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
453                try (ProgressMonitor membersMonitor = progressMonitor.split("Loading project members of " + projectId, 1)) {
454                        ProjectApi projectApi = gitLabApi.getProjectApi();
455                        Pager<org.gitlab4j.api.models.Member> memberPager = projectApi.getMembers(projectId, getPageSize());
456                        int pageNum = 0;
457                        while (memberPager.hasNext()) {
458                                ++pageNum;
459                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
460                                try (ProgressMonitor memberPageMonitor = membersMonitor.split("Project members page " + pageNum, monitorSize)) {
461                                        List<org.gitlab4j.api.models.Member> members = memberPager.next();
462                                        try (ProgressMonitor scaledMembersMonitor = memberPageMonitor.scale(members.size() * 2 + 1)) {
463                                                scaledMembersMonitor.worked(Status.INFO, 1, "Retrieved " + members.size() + " members");
464                                                for (org.gitlab4j.api.models.Member member: members) {
465                                                        try (ProgressMonitor memberMonitor = scaledMembersMonitor.split("Loading member " + member.getName() + " " + member.getId(), 2, member)) {
466                                                                org.nasdanika.models.gitlab.Member modelMember = loadMember(member, memberMonitor.split("Loading member data", 1));
467                                                                memberConsumer.accept(modelMember, memberMonitor.split("Consuming member", 1));
468                                                        }
469                                                }
470                                        }
471                                }
472                        }
473                }
474        }
475        
476        public void loadProjectContributors(long projectId, BiConsumer<org.nasdanika.models.gitlab.Contributor, ProgressMonitor> contributorConsumer, ProgressMonitor progressMonitor) throws GitLabApiException {
477                try (ProgressMonitor contributorsMonitor = progressMonitor.split("Loading project contributors of " + projectId, 1)) {
478                        RepositoryApi repoApi = gitLabApi.getRepositoryApi();
479                        Pager<org.gitlab4j.api.models.Contributor> contributorPager = repoApi.getContributors(projectId, getPageSize());
480                        int pageNum = 0;
481                        while (contributorPager.hasNext()) {
482                                ++pageNum;
483                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
484                                try (ProgressMonitor contributorPageMonitor = contributorsMonitor.split("Project contributors page " + pageNum, monitorSize)) {
485                                        List<org.gitlab4j.api.models.Contributor> contributors = contributorPager.next();
486                                        try (ProgressMonitor scaledContributorsMonitor = contributorPageMonitor.scale(contributors.size() + 1)) {
487                                                scaledContributorsMonitor.worked(Status.INFO, 1, "Retrieved " + contributors.size() + " contributors");
488                                                for (org.gitlab4j.api.models.Contributor contributor: contributors) {
489                                                        try (ProgressMonitor contributorMonitor = scaledContributorsMonitor.split("Loading contributor " + contributor.getName() + " " + contributor.getId(), 1, contributor)) {
490                                                                org.nasdanika.models.gitlab.Contributor modelContributor = createContributor(contributor, contributorMonitor);
491                                                                populateAbstractUser(contributor, modelContributor);
492                                                                modelContributor.setAdditions(contributor.getAdditions());
493                                                                modelContributor.setCommits(contributor.getCommits());
494                                                                modelContributor.setDeletions(contributor.getDeletions());
495                                                                contributorConsumer.accept(modelContributor, contributorMonitor);
496                                                        }
497                                                }
498                                        }
499                                }
500                        }
501                }
502        }
503                        
504//      CommitsApi commitsApi = gitLabApi.getCommitsApi();
505//      for (Commit commit: commitsApi.getCommits(project.getId())) {
506//              System.out.println("Commit: " + commit);
507//      }
508                        
509        public void loadBranches(
510                        long projectId, 
511                        BiConsumer<org.nasdanika.models.gitlab.Branch, ProgressMonitor> branchConsumer, 
512                        ProgressMonitor progressMonitor) throws GitLabApiException {
513                
514                try (ProgressMonitor branchesMonitor = progressMonitor.split("Loading branches of " + projectId, 1)) {
515                        RepositoryApi repoApi = gitLabApi.getRepositoryApi();
516                        Pager<org.gitlab4j.api.models.Branch> branchPager = repoApi.getBranches(projectId, getPageSize());
517                        int pageNum = 0;
518                        while (branchPager.hasNext()) {
519                                ++pageNum;
520                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
521                                try (ProgressMonitor branchPageMonitor = branchesMonitor.split("Branch page " + pageNum, monitorSize)) {
522                                        List<org.gitlab4j.api.models.Branch> branches = branchPager.next();
523                                        try (ProgressMonitor scaledBranchPageMonitor = branchPageMonitor.scale(branches.size() + 1)) {
524                                                scaledBranchPageMonitor.worked(Status.INFO, 1, "Retrieved " + branches.size() + " branches");
525                                                for (org.gitlab4j.api.models.Branch branch: branches) {
526                                                        try (ProgressMonitor branchMonitor = scaledBranchPageMonitor.split("Loading branch " + branch.getName(), 1, branch)) {
527                                                                org.nasdanika.models.gitlab.Branch modelBranch = createBranch(branch, progressMonitor);
528                                                                modelBranch.setCanPush(branch.getCanPush());
529                                                                
530                                                                Commit commit = branch.getCommit();
531                                                                if (commit != null) {
532                                                                        modelBranch.setCommitDate(commit.getCommittedDate());
533                                                                }
534                                                                
535                                                                modelBranch.setDevelopersCanMerge(branch.getDevelopersCanMerge());
536                                                                modelBranch.setDevelopersCanPush(branch.getDevelopersCanPush());
537                                                                modelBranch.setIsDefault(branch.getDefault());
538                                                                modelBranch.setIsProtected(branch.getProtected());
539                                                                modelBranch.setMerged(branch.getMerged());
540                                                                modelBranch.setName(branch.getName());
541                                                                modelBranch.setWebUrl(branch.getWebUrl());
542                                                                
543                                                                branchConsumer.accept(modelBranch, branchMonitor);
544                                                        }
545                                                }
546                                        }
547                                }
548                        }
549                }
550        }
551        
552        /**
553         * Loads branch tree
554         * @param project
555         * @param groupProvider
556         * @param projectProvider
557         * @param progressMonitor
558         * @return
559         * @throws GitLabApiException 
560         */
561        public void loadTree(
562                        long projectId,
563                        String refName,
564                        String path,
565                        BiConsumer<org.nasdanika.models.gitlab.TreeItem, ProgressMonitor> treeItemConsumer,                     
566                        ProgressMonitor progressMonitor) throws GitLabApiException {
567                
568                try (ProgressMonitor treeItemsMonitor = progressMonitor.split("Loading tree items of project: " + projectId + ", refName: " + refName + ", path: " + path, 1)) {
569                        RepositoryApi repoApi = gitLabApi.getRepositoryApi();
570                        Pager<org.gitlab4j.api.models.TreeItem> treeItemPager = repoApi.getTree(projectId, path, refName, getPageSize());
571                        int pageNum = 0;
572                        while (treeItemPager.hasNext()) {
573                                ++pageNum;
574                                double monitorSize = 1.0/Math.pow(2.0, pageNum); // Unknown number of pages, dividing each next by 2. I.e. 1/2 for the first page, 1/4 for the second, ...
575                                try (ProgressMonitor treeItemPagePageMonitor = treeItemsMonitor.split("Tree item page " + pageNum, monitorSize)) {
576                                        List<org.gitlab4j.api.models.TreeItem> treeItems = treeItemPager.next();
577                                        try (ProgressMonitor scaledTreeItemPageMonitor = treeItemPagePageMonitor.scale(treeItems.size() + 1)) {
578                                                scaledTreeItemPageMonitor.worked(Status.INFO, 1, "Retrieved " + treeItems.size() + " tree items");
579                                                for (org.gitlab4j.api.models.TreeItem treeItem: treeItems) {
580                                                        try (ProgressMonitor treeItemMonitor = scaledTreeItemPageMonitor.split("Loading tree item " + treeItem.getName(), 1, treeItem)) {
581                                                                switch (treeItem.getType()) {
582                                                                case TREE:
583                                                                        org.nasdanika.models.gitlab.Tree subTree = createTree(projectId, refName, treeItem);
584                                                                        if (subTree != null) {
585                                                                                subTree.setId(treeItem.getId());
586                                                                                subTree.setName(treeItem.getName());
587                                                                                subTree.setPath(treeItem.getPath());
588                                                                                treeItemConsumer.accept(subTree, treeItemMonitor);
589                                                                        }
590                                                                        break;
591                                                                case BLOB:
592                                                                        org.nasdanika.models.gitlab.Blob blob = createBlob(
593                                                                                        projectId, 
594                                                                                        refName, 
595                                                                                        treeItem, 
596                                                                                        progressMonitor);
597                                                                        if (blob != null) {
598                                                                                blob.setId(treeItem.getId());
599                                                                                blob.setName(treeItem.getName());
600                                                                                blob.setPath(treeItem.getPath());
601                                                                                treeItemConsumer.accept(blob, treeItemMonitor);
602                                                                        }
603                                                                        break;
604                                                                case COMMIT:
605                                                                        break;
606                                                                default:
607                                                                        break;
608                                                                
609                                                                }                                                               
610                                                        }
611                                                }
612                                        }
613                                }
614                        }
615                }
616        }
617        
618        /**
619         * Creates a model tree. Override to create specialized trees.
620         * @param modelProject
621         * @param modelBranch
622         * @param tree
623         * @return
624         */
625        protected org.nasdanika.models.gitlab.Tree createTree(long projectId, String refName, TreeItem tree) {          
626                return getFactory().createTree();
627        }
628                        
629        /**
630         * Creates and populates a model blob. This implementation returns RepositoryFile. Override to create specialized blobs, e.g. TextRepositoryFile.
631         * This method may return null or an instance of org.nasdanika.models.gitlab.Blob to avoid calling to repository file API. 
632         * @param modelProject
633         * @param modelBranch
634         * @param blob
635         * @return
636         * @throws GitLabApiException 
637         */
638        protected org.nasdanika.models.gitlab.Blob createBlob(
639                        long projectId, 
640                        String refName, 
641                        TreeItem blob,                  
642                        ProgressMonitor progressMonitor) throws GitLabApiException {
643
644                org.nasdanika.models.gitlab.RepositoryFile ret = createRepositoryFile(
645                                projectId, 
646                                refName, 
647                                blob, 
648                                progressMonitor);
649                
650                ret.setCommitId(ret.getCommitId());
651                ret.setLastCommitId(ret.getLastCommitId());
652                ret.setRef(ret.getRef());
653                ret.setSize(ret.getSize());
654                return ret;
655        }
656        
657        /**
658         * Creates a model repository file. This implementation returns RepositoryFile. Override to create specialized blobs, e.g. TextRepositoryFile.
659         * @param modelProject
660         * @param modelBranch
661         * @param blob
662         * @param repositoryFile
663         * @return
664         */
665        protected org.nasdanika.models.gitlab.RepositoryFile createRepositoryFile(
666                        long projectId, 
667                        String refName, 
668                        TreeItem blob,                  
669                        ProgressMonitor progressMonitor) {
670
671                return getFactory().createRepositoryFile();
672        }
673
674        public org.gitlab4j.api.models.RepositoryFile loadRepositoryFile(long projectId, String path, String refName) throws GitLabApiException {
675                RepositoryFileApi repoFileApi = gitLabApi.getRepositoryFileApi();
676                return repoFileApi.getFile(projectId, path, refName);
677        }
678        
679}