/*******************************************************************************
* Copyright (c) 2013, 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.wizard.content;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubMonitor;
import org.springframework.ide.eclipse.boot.wizard.github.GithubClient;
import org.springframework.ide.eclipse.boot.wizard.github.Repo;
import org.springsource.ide.eclipse.commons.core.preferences.StsProperties;
import org.springsource.ide.eclipse.commons.frameworks.core.downloadmanager.DownloadManager;
/**
* Singleton class. The instance of this class provides access to all the
* getting started content.
*
* NOTE: templates are not (yet?) included in this. Code to discover and
* manage them already existed before this framework was implemented.
*/
public class GettingStartedContent extends ContentManager {
// IMPORTANT NOTE: Because this is a singleton class,
// CARE needs to be taken with any listeners registered, especially in live expressions.
// The ContentManager super class has two tracker LiveVariables to track
// registration of content providers and downloading of content.
// These two are publicly exposed and may result in listeners being registered by owners.
// To avoid memory leaks, be sure that any live expressions that accumulate listeners are properly disposed
private static GettingStartedContent INSTANCE = null;
private final static boolean ADD_REAL = true;
private final static boolean ADD_MOCKS = false; // (""+Platform.getLocation()).contains("kdvolder")
private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder")
|| (""+Platform.getLocation()).contains("bamboo");
public static GettingStartedContent getInstance() {
if (INSTANCE == null) {
INSTANCE = new GettingStartedContent();
}
return INSTANCE;
}
@Override
protected void prefetch(IProgressMonitor monitor) {
// Register the content providers as part of prefetching, as registering
// the providers may also require network access much like downloading content.
String registeringProvidersLabel = "Registering content providers";
registerAllContentProviders(SubMonitor.convert(monitor, registeringProvidersLabel, 50));
super.prefetch(monitor);
}
private final GithubClient github = new GithubClient();
/**
* We need this in multiple places. So cache it to avoid asking for it multiple times in a row.
*/
private Repo[] cachedRepos = null;
private Repo[] getGuidesRepos() {
if (cachedRepos==null) {
Repo[] repos = github.getOrgRepos("spring-guides");
Arrays.sort(repos, new Comparator<Repo>() {
public int compare(Repo o1, Repo o2) {
return o1.getName().compareTo(o2.getName());
}
});
if (DEBUG) {
// System.out.println("==== spring-guides-repos ====");
// int count = 1;
// for (Repo r : repos) {
// System.out.println(count++ + ":" + r.getName());
// }
// System.out.println("==== spring-guides-repos ====");
}
cachedRepos = repos;
}
return cachedRepos;
}
/**
* Registering content providers may require network access if content provider properties
* need to be fetched external. Avoid running in UI thread.
* @param monitor must not be null.
*/
protected void registerAllContentProviders(IProgressMonitor monitor) {
// Avoid registering and downloading content properties if already registered
if (prefetchContentProviderPropertiesTracker.getValue()!=DownloadState.DOWNLOADED) {
try {
prefetchContentProviderPropertiesTracker.setValue(DownloadState.IS_DOWNLOADING);
registerAllWithStsProperties(StsProperties.getInstance(monitor));
prefetchContentProviderPropertiesTracker.setValue(DownloadState.DOWNLOADING_COMPLETED);
} finally {
prefetchContentProviderPropertiesTracker.setValue(DownloadState.DOWNLOADED);
}
}
}
/**
* Will register all content providers using STS properties.
*
*/
public void registerAllWithStsProperties(final StsProperties stsProps) {
//Guides: are discoverable because they are all repos in org on github
register(GettingStartedGuide.class, GettingStartedGuide.GUIDE_DESCRIPTION_TEXT,
new ContentProvider<GettingStartedGuide>() {
// @Override
public GettingStartedGuide[] fetch(DownloadManager downloader) {
LinkedHashMap<String, GettingStartedGuide> guides = new LinkedHashMap<String, GettingStartedGuide>();
if (ADD_MOCKS) {
addGuidesFrom(github.getMyRepos(), guides, downloader);
}
if (ADD_REAL) {
addGuidesFrom(getGuidesRepos(), guides, downloader);
}
return guides.values().toArray(new GettingStartedGuide[guides.size()]);
}
private LinkedHashMap<String, GettingStartedGuide> addGuidesFrom(Repo[] repos, LinkedHashMap<String, GettingStartedGuide> guides, DownloadManager downloader) {
for (Repo repo : repos) {
String name = repo.getName();
// System.out.println("repo : "+name + " "+repo.getUrl());
if (name.startsWith("gs-") && !guides.containsKey(name)) {
guides.put(name, new GettingStartedGuide(stsProps, repo, downloader));
}
}
return guides;
}
}
);
// Commented out: there are no more tutorial guides.
// register(TutorialGuide.class, TutorialGuide.GUIDE_DESCRIPTION_TEXT,
// new ContentProvider<TutorialGuide>() {
// public TutorialGuide[] fetch(DownloadManager downloader) {
// LinkedHashMap<String, TutorialGuide> guides = new LinkedHashMap<String, TutorialGuide>();
// addGuidesFrom(getGuidesRepos(), guides, downloader);
// return guides.values().toArray(new TutorialGuide[guides.size()]);
// }
//
// private LinkedHashMap<String, TutorialGuide> addGuidesFrom(Repo[] repos, LinkedHashMap<String, TutorialGuide> guides, DownloadManager downloader) {
// for (Repo repo : repos) {
// String name = repo.getName();
// if (name.startsWith("tut-") && !guides.containsKey(name)) {
// guides.put(name, new TutorialGuide(stsProps, repo, downloader));
// }
// }
// return guides;
// }
// }
// );
//References apps: are discoverable because we maintain a list of json metadata
//that can be downloaded from some external url.
register(ReferenceApp.class, ReferenceApp.REFERENCE_APP_DESCRIPTION,
new ContentProvider<ReferenceApp>() {
// @Override
public ReferenceApp[] fetch(DownloadManager downloader) {
ReferenceAppMetaData[] infos = github.get(stsProps.get("spring.reference.app.discovery.url"), ReferenceAppMetaData[].class);
ReferenceApp[] apps = new ReferenceApp[infos.length];
for (int i = 0; i < apps.length; i++) {
//TODO: it could be quite costly to create all these since each one
// entails a request to obtain info about github repo.
// Maybe this is a good reason to put a bit more info in the
// json metadata and thereby avoid querying github to fetch it.
apps[i] = create(downloader, infos[i]);
}
return apps;
}
private ReferenceApp create(DownloadManager dl, ReferenceAppMetaData md) {
return new ReferenceApp(md, dl, github);
}
});
}
/**
* Get all getting started guides.
*/
public GettingStartedGuide[] getGSGuides() {
return get(GettingStartedGuide.class);
}
/**
* Get all tutorial guides
*/
public TutorialGuide[] getTutorials() {
return get(TutorialGuide.class);
}
public ReferenceApp[] getReferenceApps() {
return get(ReferenceApp.class);
}
/**
* Get all guide content (i.e. tutorials + gs)
*/
public GithubRepoContent[] getAllGuides() {
ArrayList<GithubRepoContent> all = new ArrayList<GithubRepoContent>();
all.addAll(asList(getTutorials()));
all.addAll(asList(getGSGuides()));
return all.toArray(new GithubRepoContent[all.size()]);
}
private <A> Collection<A> asList(A[] tutorials) {
if (tutorials==null) {
return Collections.EMPTY_LIST;
} else {
return Arrays.asList(tutorials);
}
}
// public GettingStartedGuide getGuide(String guideName) {
// GettingStartedGuide[] guides = getGuides();
// if (guides!=null) {
// for (GettingStartedGuide g : guides) {
// if (guideName.equals(g.getName())) {
// return g;
// }
// }
// }
// return null;
// }
}