blob: 45bdab6776207f9f33b3df34db2246bffe92bd39 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomee.website;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.openejb.loader.IO;
import org.jbake.app.Parser;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class GroupedIndex {
private final File directory;
private final String type;
public GroupedIndex(final File directory, final String type) {
this.directory = directory;//target/jbake/<tomeeBranch> or target/jbake/<tomeeBranch>/docs
this.type = type;
}
public static void process(final File directory, final String type) {
new GroupedIndex(directory, type).process();
}
public void process() {
final List<Doc> docs = list(directory);
final List<String> detectedLanguages = new ArrayList<>();
Set<String> uniqueValues = new HashSet<>();
for (Doc doc1 : docs) {
if (uniqueValues.add(doc1.language)) {
detectedLanguages.add(doc1.language);
}
}
for (String language : detectedLanguages) {
sortSectionsAndCreateIndexFiles(docs, language);
}
}
private void sortSectionsAndCreateIndexFiles(List<Doc> docs, String language) {
final Map<String, List<Doc>> sections = new HashMap<>();
//filtering only documents with the same language for: target/jbake/<tomeeBranch>/docs
if (this.type.equalsIgnoreCase("docsindex")) {
for (Doc doc1 : docs) {
if (doc1.language.equalsIgnoreCase(language)) {
sections.computeIfAbsent(doc1.getGroup(), k -> new ArrayList<>()).add(doc1);
}
}
} else {
if (this.type.equalsIgnoreCase("examplesindex")) {
for (Doc doc1 : docs) { //filtering only documents with the same language for: target/jbake/<tomeeBranch> and type = examplesindex
if (doc1.language.equalsIgnoreCase(language) && doc1.source.getParentFile().getName().equalsIgnoreCase("examples")) {
sections.computeIfAbsent(doc1.getGroup(), k -> new ArrayList<>()).add(doc1);
}
}
} else {//any type (used in GroupedIndexTest.java
for (Doc doc1 : docs) {
if (doc1.language.equalsIgnoreCase(language)) {
sections.computeIfAbsent(doc1.getGroup(), k -> new ArrayList<>()).add(doc1);
}
}
}
}
final List<String> sectionsFormatted = new ArrayList<>();
/**
* We want to sort the sections with the most entries towards the top.
* Unless it is crazy big, then we put it in a special section at the bottom.
*/
sections.entrySet().stream()
.filter(entry -> entry.getValue().size() < 10)
.sorted((o1, o2) -> new Integer(o2.getValue().size()).compareTo(o1.getValue().size()))
.forEach(entry -> {
final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
final PrintStream out = new PrintStream(bytes);
out.printf(" <div class=\"group-title\">%s</div>\n", entry.getKey());
out.printf(" <ul class=\"group\">\n");
entry.getValue().stream().sorted().forEach(doc -> {
out.printf(
" <li class=\"group-item\"><span class=\"group-item-i\" ><i class=\"fa fa-angle-right\"></i></span><a href=\"%s\">%s</a></li>\n",
doc.getHref(), doc.getTitle());
});
out.printf(" </ul>\n");
sectionsFormatted.add(new String(bytes.toByteArray()));
});
try (final PrintStream out = print(directory.getAbsolutePath(), "index.html", language, this.type)) {
out.printf("type=%s\n", type);
out.printf("status=published\n");
out.printf("~~~~~~\n");
{
final ListIterator<String> iterator = sectionsFormatted.listIterator();
while (iterator.hasNext()) {
out.printf(" <div class=\"row\">\n");
out.printf(" <div class=\"col-md-4\">\n");
out.printf(iterator.hasNext() ? iterator.next() : "");
out.printf(" </div>\n");
out.printf(" <div class=\"col-md-4\">\n");
out.printf(iterator.hasNext() ? iterator.next() : "");
out.printf(" </div>\n");
out.printf(" <div class=\"col-md-4\">\n");
out.printf(iterator.hasNext() ? iterator.next() : "");
out.printf(" </div>\n");
out.printf(" </div>\n");
}
}
sections.entrySet().stream()
.filter(entry -> entry.getValue().size() >= 10)
.sorted((o1, o2) -> new Integer(o1.getValue().size()).compareTo(o2.getValue().size()))
.forEach(entry -> {
out.printf(" <div class=\"row\">\n");
out.printf(" <div class=\"col-md-12\">\n");
out.printf(" <div class=\"group-title large\">%s</div>\n", entry.getKey());
out.printf(" </div>\n");
out.printf(" </div>\n");
final ListIterator<Doc> iterator = entry.getValue().stream()
.sorted()
.collect(Collectors.toList())
.listIterator();
final int i = (int) Math.ceil(entry.getValue().size() / 3f);
out.printf(" <div class=\"row\">\n");
while (iterator.hasNext()) {
int count = 0;
out.printf(" <div class=\"col-md-4\">\n");
out.printf(" <ul class=\"group\">\n");
while (iterator.hasNext() && count++ < i) {
final Doc doc = iterator.next();
out.printf(
" <li class=\"group-item\"><span class=\"group-item-i\" ><i class=\"fa fa-angle-right\"></i></span><a href=\"%s\">%s</a></li>\n",
doc.getHref(), doc.getTitle());
}
out.printf(" </ul>\n");
out.printf(" </div>\n");
}
out.printf(" </div>\n");
});
} catch (Exception e) {
throw e; //ToDo: implementing proper logger and catch.
}
}
public static PrintStream print(final String directory, final String child, String language, String type) { //target/jbake/<tomeeBranch> o tambien //target/jbake/<tomeeBranch>/docs
try {
File fileParentFolder = null;
if (type.equalsIgnoreCase("docsindex")) {
fileParentFolder = new File(directory);
} else {
if (type.equalsIgnoreCase("examplesindex")) {
if (language.equalsIgnoreCase("en")) {
fileParentFolder = new File(directory + File.separator + "examples");
} else {
fileParentFolder = new File(directory + File.separator + language + File.separator + "examples");
}
} else {
fileParentFolder = new File(directory);
}
}
return new PrintStream(IO.write(new File(fileParentFolder, child)));
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
}
public List<Doc> list(final File directory) {//target/jbake/<tomeeBranch>
try {
return Files.walk(directory.toPath())
.map(Path::toFile)
.filter(File::isFile)
.filter(Docs::isRendered)
.map(this::parse)
.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String getLanguageFromPath(File file, String type) { //target/jbake/<tomeeBranch>/fr/examples/index.html
if (type.equalsIgnoreCase("docsindex")) { //ToDo: this needs to be updated when we are going to proccess docs internationalization too.
return "";
} else { //examplesindex
if (file.getParentFile().getParentFile().getName().length() > 3) { // target/jbake/<tomeeBranchLengthIsGratherThan3>/examples/index.html
return "en";
} else {
return file.getParentFile().getParentFile().getName(); // target/jbake/<tomeeBranch>/fr/examples/index.html
}
}
}
public Doc parse(final File file) {
final Parser parser = new Parser(new CompositeConfiguration(), file.getAbsolutePath());
final Map<String, Object> map = parser.processFile(file);
if (map == null) {
return new Doc("Unknown", Docs.simpleName(file), Docs.href(directory, file), file);
}
final String title = getTitle(map, file);
final String group = Optional.ofNullable(map.get("index-group")).orElse("Unknown") + "";
/*
Create the document reference to be ready to be added as part of the index files like:
target/jbake/content/tomee-8.0/docs/index.html
target/jbake/content/tomee-8.0/docs/maven/index.html
target/jbake/content/tomee-8.0/fr/examples/index.html
target/jbake/content/tomee-8.0/examples/index.html
*/
if (type.equalsIgnoreCase("examplesindex") && file.getParentFile().getName().equalsIgnoreCase("examples")) {
String detectedLanguage = getLanguageFromPath(file, this.type);
if (detectedLanguage.equalsIgnoreCase("en")) {
return new Doc(group, title, Docs.href(new File(directory + File.separator + "examples"), file), file, detectedLanguage);
} else {
return new Doc(group, title, Docs.href(new File(directory + File.separator + detectedLanguage + File.separator + "examples"), file), file, detectedLanguage);
}
} else {
// todo: Here we can implement later when doc type is docindex and not examplesindex
return new Doc(group, title, Docs.href(directory, file), file); //default to english
}
}
private String getTitle(final Map<String, Object> map, final File file) {
if (hasValue(map.get("short-title"))) return map.get("short-title") + "";
if (hasValue(map.get("title"))) return map.get("title") + "";
return Docs.simpleName(file);
}
private boolean hasValue(final Object o) {
if (o == null) return false;
if ("".equals(o + "")) return false;
return true;
}
public static class Doc implements Comparable<Doc> {
@Override
public int hashCode() {
return Objects.hash(language);
}
private final String group;
private final String title;
private final String href;
private final File source;
private final String language;
public Doc(final String group, final String title, final String href, final File source) {
this.group = group;
this.title = title;
this.href = href;
this.source = source;
this.language = "en";
}
public Doc(final String group, final String title, final String href, final File source,
final String language) {
this.group = group;
this.title = title;
this.href = href;
this.source = source;
this.language = language;
}
public String getLanguage() {
return language;
}
public String getGroup() {
return group;
}
public String getTitle() {
return title;
}
public String getHref() {
return href;
}
public File getSource() {
return source;
}
@Override
public int compareTo(final Doc o) {
return this.title.toLowerCase().compareTo(o.title.toLowerCase());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Doc doc = (Doc) o;
return Objects.equals(language, doc.language);
}
}
}