blob: 3b66e3baf86e8cb90cc29e983ebc19a5f26057b6 [file] [log] [blame]
/**
* Copyright (c) 2018 Eclipse contributors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*/
package org.eclipse.emf.releng;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.releng.UpdateSiteGenerator.RepositoryAnalyzer;
/**
* A utility class used by the {@code index.htmljet} template to generate an index.html.
*/
public class UpdateSiteIndexGenerator
{
/**
* The ordered folders that will be indexed.
*/
private static final String[] ROOT_FOLDERS = new String []{ "release", "milestone", "nightly" };
/**
* The folder for this generator..
*/
private final File folder;
/**
* The update site generator used by this index generator.
*/
private final UpdateSiteGenerator updateSiteGenerator = new UpdateSiteGenerator();
/**
* The repository analyzer used to analyze the index generator's folder.
*/
private final RepositoryAnalyzer repositoryAnalyzer;
/**
* The list of SDKs in this folder's repository.
*/
private List<String> sdks;
/**
* Creates an instance for the given folder.
*
* @param folder the folder.
*/
public UpdateSiteIndexGenerator(String folder)
{
this.folder = new File(folder);
List<String> folders = new ArrayList<String>();
if (isRoot())
{
for (String child : ROOT_FOLDERS)
{
File childFolder = new File(folder, child);
if (childFolder.isDirectory())
{
folders.add(childFolder.getAbsolutePath());
}
}
}
else
{
folders.add(folder);
}
repositoryAnalyzer = updateSiteGenerator.getRepositoryAnalyzer(folders);
}
/**
* Returns whether this folder is the build root folder.
* @return whether this folder is the build root folder.
*/
public boolean isRoot()
{
return folder.equals(new File(UpdateSiteGenerator.BUILDS_ROOT_FOLDER));
}
/**
* Returns the build type of this folder.
* It's not appropriate to call this method on the root folder.
* @return the build type of this folder.
*/
public String getBuildType()
{
for (String buildType : UpdateSiteGenerator.BUILD_TYPES)
{
if (folder.getPath().replace('\\', '/').contains("/" + buildType))
{
return buildType;
}
}
throw new IllegalStateException("The folder '" + folder + "' is not a build-type folder");
}
/**
* Returns whether is a latest folder.
* @return whether is a latest folder.
*/
public boolean isLatest()
{
return folder.getPath().endsWith("latest");
}
/**
* Returns whether this folder has a download archive along with associated SHA digests.
* @return
*/
public boolean hasArchive()
{
File archiveFile = UpdateSiteGenerator.getArchiveFile(folder.getPath());
return archiveFile.isFile() && UpdateSiteGenerator.getDigestFile(archiveFile.getPath(), "SHA-256").isFile()
&& UpdateSiteGenerator.getDigestFile(archiveFile.getPath(), "SHA-512").isFile();
}
/**
* Returns the download archive location.
* @return the download archive location.
*/
public String getArchive()
{
File archiveFile = UpdateSiteGenerator.getArchiveFile(folder.getPath());
return archiveFile.getPath();
}
/**
* Returns the URL to be used for loading the archive.
* @return the URL to be used for loading the archive.
*/
public String getArchiveDownloadURL()
{
File archiveFile = UpdateSiteGenerator.getArchiveFile(folder.getPath());
return "https://www.eclipse.org/downloads/download.php?file=/" + archiveFile.getPath().substring(UpdateSiteGenerator.DOWLOAD_ROOT_FOLDER.length()).replace('\\', '/');
}
/**
* Returns the digest location for the given algorithm.
* @param algorithm the algorithm of the digest.
* @return the digest location for the given algorithm.
*/
public String getDigest(String algorithm)
{
File archiveFile = UpdateSiteGenerator.getArchiveFile(folder.getPath());
File digestFile = UpdateSiteGenerator.getDigestFile(archiveFile.getPath(), algorithm);
return digestFile.getPath();
}
/**
* Returns the map used to populate the navigation sidebar.
* @return the map used to populate the navigation sidebar.
*/
public Map<String, String> getNavigation()
{
String siteURL = getSiteURL();
UpdateSiteIndexGenerator root = new UpdateSiteIndexGenerator(UpdateSiteGenerator.BUILDS_ROOT_FOLDER);
String rootSiteURL = root.getSiteURL();
StringBuilder prefix = new StringBuilder();
for (int index = siteURL.indexOf('/', rootSiteURL.length()); index != -1; index = siteURL.indexOf('/', index + 1))
{
prefix.append("../");
}
Map<String, String> result = new LinkedHashMap<String, String>();
result.put(prefix.length() == 0 ? "." : prefix.toString().substring(0, prefix.length() - 1), isRoot() ? "Builds@" : "Builds");
for (UpdateSiteIndexGenerator child : root.getChildren())
{
child.visit(result, prefix.toString(), 0);
}
URI rootSiteURI = URI.create(siteURL + "/");
for (Map.Entry<String, String> entry : result.entrySet())
{
String url = entry.getKey();
URI resolvedURI = rootSiteURI.resolve(url);
if (resolvedURI.toString().equals(siteURL))
{
entry.setValue(entry.getValue() + "@");
}
}
return result;
}
/**
* Used to composite the {@link #getNavigation() navigation} sidebar.
* @param navigation the map to populate.
* @param prefix the prefix used to build relative URL.
* @param depth the depth at which we're currently visiting.
*/
private void visit(Map<String, String> navigation, String prefix, int depth)
{
StringBuilder label = new StringBuilder();
for (int i = 0; i < depth; ++i)
{
label.append('-');
}
label.append(getLabel());
String siteURL = getSiteURL();
String rootSiteURL = getSiteURL(new File(UpdateSiteGenerator.BUILDS_ROOT_FOLDER));
siteURL = siteURL.substring(rootSiteURL.length() + 1);
siteURL = prefix + siteURL;
navigation.put(siteURL, label.toString());
for (UpdateSiteIndexGenerator child : getChildren())
{
child.visit(navigation, prefix, depth + 1);
}
}
/**
* Returns the site URL of this folder.
* @return the site URL of this folder.
*/
public String getSiteURL()
{
return getSiteURL(folder);
}
/**
* This is specialized to ensure that on the real build host we use the https: URL not the file: URL that we use locally when testing.
* @param folder the folder for which we want the site URL.
* @return the site URL.
*/
private static String getSiteURL(File folder)
{
String path = folder.getPath();
if (UpdateSiteGenerator.DOWLOAD_ROOT_FOLDER.equals(UpdateSiteGenerator.DOWNLOAD_ECLIPSE_ORG_FOLDER))
{
return "https://download.eclipse.org/" + path.substring(UpdateSiteGenerator.DOWLOAD_ROOT_FOLDER.length());
}
else
{
return UpdateSiteGenerator.createURI(folder).toString();
}
}
/**
* The parent-relative URL for this folder's index.
* @return the parent-relative URL for this folder's index.
*/
public String getRelativeIndexURL()
{
return folder.getName() + "/index.html";
}
/**
* This is used to populate the breadcumbs.
* @return the breadcumbs.
*/
public Map<String, String> getBreadcrumbs()
{
File root = new File(UpdateSiteGenerator.BUILDS_ROOT_FOLDER);
// Compute the labels in the right order.
List<String> labels = new ArrayList<String>();
for (File file = folder; file.getParentFile() != null; file = file.getParentFile())
{
if (file.equals(root))
{
labels.add(0, "Builds");
break;
}
else
{
String name = file.getName();
labels.add(0, Character.toUpperCase(name.charAt(0)) + name.substring(1));
}
}
// Compute the uplinks in the reverse order.
Map<String, String> result = new LinkedHashMap<String, String>();
String link = null;
for (int i = labels.size() - 1; i >= 0; --i)
{
String label = labels.get(i);
result.put(label, link);
if (link == null)
{
link = "../index.html";
}
else
{
link = "../" + link;
}
}
// Build another map in the right order.
Map<String, String> breadcumbs = new LinkedHashMap<String, String>();
for (String label : labels)
{
breadcumbs.put(label, result.get(label));
}
return breadcumbs;
}
/**
* Returns the title for this folder's index.
* @return the title for this folder's index.
*/
public String getTitle()
{
if (isRoot())
{
return "EMF Update Sites";
}
else
{
String name = repositoryAnalyzer.getName();
String folderName = folder.getName();
if (!name.toLowerCase().contains(folderName.toLowerCase()))
{
name += " - " + Character.toUpperCase(folderName.charAt(0)) + folderName.substring(1);
}
return name;
}
}
/**
* Returns the title case label for this folder.
* @return the title case label for this folder.
*/
public String getLabel()
{
String name = getFolderName();
return Character.toUpperCase(name.charAt(0)) + name.substring(1);
}
/**
* Returns a map from repository URIs to relative site URLs of that repository.
* @return
*/
public Map<String, String> getRepositoryChildren()
{
List<String> repositories = repositoryAnalyzer.getRawChildren();
if (repositories != null)
{
Map<String, String> result = new LinkedHashMap<String, String>();
URI siteURL = URI.create(getSiteURL() + "/");
for (String repository : repositories)
{
URI uri = URI.create(repository);
if (uri.isAbsolute() || uri.isOpaque())
{
throw new IllegalStateException("Bad non-relative URI" + uri);
}
URI resolvedURI = siteURL.resolve(uri);
result.put(resolvedURI.toString(), repository);
}
return result;
}
else
{
return null;
}
}
/**
* Returns the name of this folder.
* @return the name of this folder.
*/
public String getFolderName()
{
return folder.getName();
}
/**
* Returns the SDKs in this folder's repository.
* @return the SDKs in this folder's repository.
*/
public List<String> getSDKs()
{
if (sdks == null)
{
sdks = repositoryAnalyzer.getSDKs();
}
return sdks;
}
/**
* Returns whether the given feature is an SDK.
* @param feature the feature.
* @return whether the given feature is an SDK.
*/
public boolean isSDK(String feature)
{
for (String sdk : getSDKs())
{
if (feature.startsWith(sdk))
{
return true;
}
}
return false;
}
/**
* Returns the feature names in this folder's repository.
* @return the feature names in this folder's repository.
*/
public List<String> getFeatures()
{
return repositoryAnalyzer.getFeatures();
}
/**
* Returns the bundle information for this folder's repository.
* @return the bundle information for this folder's repository.
*/
public Map<String, List<String>> getBundles()
{
return repositoryAnalyzer.getBundles();
}
public String getFolderID(String folder)
{
return "_" + new File(folder).getName().replace('.', '_');
}
/**
* Returns the ordered index generators for the children of this folder.
* @return the ordered index generators for the children of this folder.
*/
public List<UpdateSiteIndexGenerator> getChildren()
{
List<UpdateSiteIndexGenerator> result = new ArrayList<UpdateSiteIndexGenerator>();
File[] listFiles = folder.listFiles();
List<String> children = new ArrayList<String>();
if (listFiles != null)
{
for (File child : listFiles)
{
if (child.isDirectory() && (new File(child, "compositeContent.jar").isFile() || new File(child, "content.jar").isFile()))
{
children.add(child.getAbsolutePath());
}
}
}
UpdateSiteGenerator.sort(children);
for (String child : children)
{
result.add(new UpdateSiteIndexGenerator(child));
}
return result;
}
/**
* Returns a map from project name to the URL for the commit ID URL in that project's branding plugin.
* @return a map from project name to the URL for the commit ID URL in that project's branding plugin.
*/
public Map<String, String> getCommits()
{
return repositoryAnalyzer.getCommits();
}
/**
* Returns the date string for when the IUs in the repository were built.
* @return the date string for when the IUs in the repository were built.
*/
public String getDate()
{
return repositoryAnalyzer.getDate();
}
}