You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by vi...@apache.org on 2019/05/18 14:35:12 UTC
[netbeans-tools] branch master updated: Tutorials
This is an automated email from the ASF dual-hosted git repository.
vieiro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans-tools.git
The following commit(s) were added to refs/heads/master by this push:
new 3049f26 Tutorials
3049f26 is described below
commit 3049f26ce5bcd7adb5f59c54f2c55c4a585b8ca0
Author: vieiro <vi...@apache.org>
AuthorDate: Sat May 18 16:32:47 2019 +0200
Tutorials
---
.gitignore | 3 +
tutorials-convert/README.md | 18 +
tutorials-convert/nbactions.xml | 66 +++
tutorials-convert/pom.xml | 95 ++++
.../tools/tutorials/AsciidocPostProcessor.java | 240 +++++++++
.../tutorials/CustomAsciiDocDocumentBuilder.java | 544 +++++++++++++++++++++
...CustomAsciiDocDocumentBuilderWithoutTables.java | 47 ++
.../netbeans/tools/tutorials/ExternalLinksMap.java | 81 +++
.../netbeans/tools/tutorials/HTMLConverter.java | 268 ++++++++++
.../org/netbeans/tools/tutorials/Language.java | 66 +++
.../tools/tutorials/LocalizedTutorialSection.java | 91 ++++
.../tools/tutorials/TutorialsBundle.properties | 46 ++
.../tutorials/TutorialsBundle_es_CA.properties | 31 ++
.../tools/tutorials/TutorialsBundle_ja.properties | 31 ++
.../tutorials/TutorialsBundle_pt_BR.properties | 31 ++
.../tools/tutorials/TutorialsBundle_ru.properties | 31 ++
.../tutorials/TutorialsBundle_zh_CN.properties | 31 ++
.../tools/tutorials/index-template.mustache | 33 ++
.../tools/tutorials/section-template.mustache | 27 +
19 files changed, 1780 insertions(+)
diff --git a/.gitignore b/.gitignore
index 08e92ac..f7eea08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,6 @@
/html-convert/target/**
/html-convert/tutorials-asciidoc/**
/html-convert/external-links.txt
+/tutorials-convert/tutorials-asciidoc/**
+/tutorials-convert/target**
+/tutorials-convert/external-links.txt
diff --git a/tutorials-convert/README.md b/tutorials-convert/README.md
new file mode 100644
index 0000000..80bba1b
--- /dev/null
+++ b/tutorials-convert/README.md
@@ -0,0 +1,18 @@
+# tutorials-convert
+
+This tool reads the NetBeans tutorials in HTML format and converts them to AsciiDoc format.
+
+The NetBeans platform tutorials can be found in https://svn.netbeans.org/svn/platform~platform-content/trunk/tutorials/
+
+The NetBeans platform tutorial images can be found in https://svn.netbeans.org/svn/platform~platform-content/trunk/images/
+
+
+## Getting started
+
+1. Check out the tutorials from SVN above in a directory "X".
+2. Check out the images from SVN above in directory "Y".
+2. Run `mvn package exec:java X Y`, where "X" is the directory in the previous step.
+4. Open the `tutorials-asciidoc` directory to see the results.
+5. See the generated "external-links.txt" file to see referenced external links.
+
+NOTE: This tool is expected to be run once, after that manual revision of generated files should be done.
diff --git a/tutorials-convert/nbactions.xml b/tutorials-convert/nbactions.xml
new file mode 100644
index 0000000..24261f4
--- /dev/null
+++ b/tutorials-convert/nbactions.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<actions>
+ <action>
+ <actionName>run</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>exec:java</goal>
+ </goals>
+ <properties>
+ <exec.args>C:\Users\avieiro\Downloads\NETBEANS\</exec.args>
+ <exec.executable>java</exec.executable>
+ </properties>
+ </action>
+ <action>
+ <actionName>debug</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
+ </goals>
+ <properties>
+ <exec.args>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address} -classpath %classpath org.netbeans.tools.tutorials.HTMLConverter C:\Users\avieiro\Downloads\NETBEANS</exec.args>
+ <exec.executable>java</exec.executable>
+ <jpda.listen>true</jpda.listen>
+ </properties>
+ </action>
+ <action>
+ <actionName>profile</actionName>
+ <packagings>
+ <packaging>jar</packaging>
+ </packagings>
+ <goals>
+ <goal>process-classes</goal>
+ <goal>org.codehaus.mojo:exec-maven-plugin:1.5.0:exec</goal>
+ </goals>
+ <properties>
+ <exec.args>-classpath %classpath org.netbeans.tools.tutorials.HTMLConverter C:\Users\avieiro\Downloads\NETBEANS</exec.args>
+ <exec.executable>java</exec.executable>
+ </properties>
+ </action>
+ </actions>
diff --git a/tutorials-convert/pom.xml b/tutorials-convert/pom.xml
new file mode 100644
index 0000000..671d209
--- /dev/null
+++ b/tutorials-convert/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.netbeans.tools</groupId>
+ <artifactId>tutorials-convert</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.mylyn.docs</groupId>
+ <artifactId>org.eclipse.mylyn.wikitext</artifactId>
+ <version>3.0.20</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.mylyn.docs</groupId>
+ <artifactId>org.eclipse.mylyn.wikitext.mediawiki</artifactId>
+ <version>3.0.20</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.mylyn.docs</groupId>
+ <artifactId>org.eclipse.mylyn.wikitext.asciidoc</artifactId>
+ <version>3.0.20</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.mylyn.docs</groupId>
+ <artifactId>org.eclipse.mylyn.wikitext.html</artifactId>
+ <version>3.0.20</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.mylyn.docs</groupId>
+ <artifactId>org.eclipse.mylyn.wikitext.markdown</artifactId>
+ <version>3.0.20</version>
+ </dependency>
+ <dependency>
+ <groupId>org.asciidoctor</groupId>
+ <artifactId>asciidoctorj</artifactId>
+ <version>1.5.6</version>
+ </dependency>
+ <dependency>
+ <groupId>com.github.spullara.mustache.java</groupId>
+ <artifactId>compiler</artifactId>
+ <version>0.9.5</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.5.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>org.netbeans.tools.tutorials.HTMLConverter</mainClass>
+ <arguments>
+ <argument>/home/antonio/REPOS/netbeans-tutorials</argument>
+ <argument>/home/antonio/REPOS/images/</argument>
+ </arguments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/AsciidocPostProcessor.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/AsciidocPostProcessor.java
new file mode 100644
index 0000000..aa0abc4
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/AsciidocPostProcessor.java
@@ -0,0 +1,240 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.Mustache;
+import com.github.mustachejava.MustacheFactory;
+import com.github.mustachejava.functions.BundleFunctions;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class AsciidocPostProcessor {
+
+ private static final Logger LOG = Logger.getLogger(AsciidocPostProcessor.class.getName());
+
+ private static enum ContentSectionState {
+ BEFORE_CONTENT_SECTION,
+ INSIDE_CONTENT_SECTION,
+ AFTER_CONTENT_SECTION
+ };
+
+ private static Pattern TITLE_PATTERN = Pattern.compile("^= (.*)$");
+
+ private static Pattern EN_CONTENTS_PATTERN = Pattern.compile("^.*Contents.*");
+
+ private static boolean isContentsHeader(String line) {
+ return line.contains("*Content")
+ || line.contains("目录")
+ || line.contains("目次")
+ || line.contains("Conteúdo")
+ || line.contains("Содержание");
+ }
+
+ /**
+ * Scans a generated AsciiDoc file and remove the "*Contents*" section with
+ * all links in it. Because the contents section is being replaced by a
+ * table of contents 'toc'.
+ *
+ * @param file
+ * @param titles
+ * @throws IOException
+ */
+ private static void cleanUpAndGetTitle(File file, HashMap<File, String> titles) throws IOException {
+ File temporaryFile = new File(file.getParentFile(), file.getName() + ".tmp");
+ ContentSectionState state = ContentSectionState.BEFORE_CONTENT_SECTION;
+ String title = null;
+
+ try (BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
+ PrintWriter output = new PrintWriter(new OutputStreamWriter(new FileOutputStream(temporaryFile), StandardCharsets.UTF_8))) {
+
+ do {
+ String line = input.readLine();
+ if (line == null) {
+ break;
+ }
+ if (title == null) {
+ Matcher m = TITLE_PATTERN.matcher(line);
+ if (m.matches()) {
+ title = m.group(1);
+ }
+ }
+ switch (state) {
+ case BEFORE_CONTENT_SECTION:
+ if (isContentsHeader(line)) {
+ state = ContentSectionState.INSIDE_CONTENT_SECTION;
+ break;
+ }
+ output.println(line);
+ break;
+ case INSIDE_CONTENT_SECTION:
+ if (line.startsWith("* <")
+ || line.startsWith("* link:")) {
+ // Ignore bullet lists with cross references or external links
+ } else {
+ output.println(line);
+ }
+ if (line.startsWith("=")) {
+ state = ContentSectionState.AFTER_CONTENT_SECTION;
+ }
+ break;
+ case AFTER_CONTENT_SECTION:
+ output.println(line);
+ break;
+ }
+ } while (true);
+
+ }
+
+ Files.move(temporaryFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+
+ if (title != null) {
+ titles.put(file, title);
+ }
+ }
+
+ static Map<File, String> removeContentSetcion(File dest) throws Exception {
+ LOG.log(Level.INFO, "Removing \"Content\" section of files...");
+
+ // Retrieve the list of asciidoc files
+ List<File> asciidocFiles = Files.find(dest.toPath(), 999,
+ (p, bfa) -> bfa.isRegularFile()).map(Path::toFile).filter((f) -> f.getName().endsWith(".asciidoc")).collect(Collectors.toList());
+
+ // Remove the 'Contents' section and fetch titles
+ HashMap<File, String> titles = new HashMap<>();
+ for (File file : asciidocFiles) {
+ cleanUpAndGetTitle(file, titles);
+ }
+
+ return titles;
+ }
+
+ /**
+ * Generates index.asciidoc, index_ja.asciidoc, index_pt_BR.asciidoc, etc.,
+ * in the nested directories, each index file has a list of links to
+ * asciidoc documents in the directory. Asciidoc documents are sorted by
+ * title, attending to the proper Locale collation rules.
+ *
+ * @param dest The destination directory.
+ * @param titles
+ * @throws IOException
+ */
+ static void generateIndexes(File dest, Map<File, String> titles) throws IOException {
+ LOG.info("Generating index.asciidoc (and translations) on all directories...");
+ /*
+ Compute the list of directories under 'dest'
+ */
+ List<File> directories = Files.find(dest.toPath(), 999,
+ (p, bfa) -> bfa.isDirectory()
+ ).map((p) -> p.toFile()).collect(Collectors.toList());
+
+ /*
+ A filter that selects documents in english (i.e., without _ja, _pt_BR, etc. suffixes).
+ */
+ FileFilter englishTutorialsFilter = (f) -> f.isFile() && Language.getLanguage(f) == Language.DEFAULT;
+
+ MustacheFactory factory = new DefaultMustacheFactory();
+ Mustache indexMustache = factory.compile("org/netbeans/tools/tutorials/index-template.mustache");
+ Mustache sectionMustache = factory.compile("org/netbeans/tools/tutorials/section-template.mustache");
+
+ /*
+ Iterate over all nexted directories...
+ */
+ for (File directory : directories) {
+ if ("images".equals(directory.getName())) {
+ continue;
+ }
+
+ HashMap<Language, List<File>> filesByLanguage = new HashMap<>();
+ /*
+ Compute the files in english
+ */
+ File[] tutorialsEnglish = directory.listFiles(englishTutorialsFilter);
+ for (File english : tutorialsEnglish) {
+
+ List<File> englishFiles = filesByLanguage.get(Language.DEFAULT);
+ if (englishFiles == null) {
+ englishFiles = new ArrayList<File>();
+ filesByLanguage.put(Language.DEFAULT, englishFiles);
+ }
+ englishFiles.add(english);
+ /*
+ And retrieve the list of translations of the english file.
+ */
+ HashMap<Language, File> translations = Language.getTranslations(english);
+ for (Map.Entry<Language, File> translation : translations.entrySet()) {
+ List<File> languageFiles = filesByLanguage.get(translation.getKey());
+ if (languageFiles == null) {
+ languageFiles = new ArrayList<>();
+ filesByLanguage.put(translation.getKey(), languageFiles);
+ }
+ languageFiles.add(translation.getValue());
+ }
+ }
+
+ for (Map.Entry<Language, List<File>> entry : filesByLanguage.entrySet()) {
+ Language language = entry.getKey();
+ if (language == Language.UNKNOWN) {
+ continue;
+ }
+ ResourceBundle bundle = ResourceBundle.getBundle("org.netbeans.tools.tutorials.TutorialsBundle", language.locale);
+ String directoryTitle = bundle.getString( directory.getName() + ".title");
+ if (directoryTitle == null) {
+ throw new IllegalArgumentException("Please add a title for directory '" + directory.getName() + "' in locale " + language.locale);
+ }
+ LocalizedTutorialSection section = new LocalizedTutorialSection(language, directoryTitle);
+ section.addAll(entry.getValue());
+ section.sort(titles);
+
+ // Generate the index
+ String name = "index" + language.extension;
+ File output = new File(directory, name);
+ try (Writer indexOutput = new OutputStreamWriter(new FileOutputStream(output), StandardCharsets.UTF_8)) {
+ indexMustache.execute(indexOutput, section);
+ }
+
+ // Also generate section.asciidoc (section_ja.asciidoc, etc.)
+ // This will be in a sidebar for all tutorials in this section
+ String sectionSidebarName = "section" + language.extension;
+ File sectionSidebarFile = new File(directory, sectionSidebarName);
+ try (Writer indexOutput = new OutputStreamWriter(new FileOutputStream(sectionSidebarFile), StandardCharsets.UTF_8)) {
+ sectionMustache.execute(indexOutput, section);
+ }
+
+ }
+
+
+ }
+ }
+
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilder.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilder.java
new file mode 100644
index 0000000..f49f291
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilder.java
@@ -0,0 +1,544 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import com.google.common.base.Joiner;
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.Strings;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+import org.eclipse.mylyn.wikitext.asciidoc.internal.AsciiDocDocumentBuilder;
+import org.eclipse.mylyn.wikitext.parser.Attributes;
+import org.eclipse.mylyn.wikitext.parser.DocumentBuilder;
+import org.eclipse.mylyn.wikitext.parser.ImageAttributes;
+import org.eclipse.mylyn.wikitext.parser.LinkAttributes;
+import org.eclipse.mylyn.wikitext.parser.builder.AbstractMarkupDocumentBuilder;
+
+/**
+ * An AsciiDocDocumentBuilder.
+ *
+ * @author Antonio Vieiro <vi...@apache.org>
+ */
+public class CustomAsciiDocDocumentBuilder extends AsciiDocDocumentBuilder {
+
+ /**
+ * Base class for asciidoc delimited blocks.
+ */
+ class AsciiDocContentBlock extends AbstractMarkupDocumentBuilder.NewlineDelimitedBlock {
+
+ protected String prefix;
+ protected String suffix;
+
+ AsciiDocContentBlock(DocumentBuilder.BlockType blockType, String prefix, String suffix) {
+ this(blockType, prefix, suffix, 1, 1);
+ }
+
+ AsciiDocContentBlock(DocumentBuilder.BlockType blockType, String prefix, String suffix, int leadingNewlines, int trailingNewlines) {
+ super(blockType, leadingNewlines, trailingNewlines);
+ this.prefix = prefix;
+ this.suffix = suffix;
+ }
+
+ AsciiDocContentBlock(String prefix, String suffix, int leadingNewlines, int trailingNewlines) {
+ this(null, prefix, suffix, leadingNewlines, trailingNewlines);
+ }
+
+ @Override
+ public void write(int c) throws IOException {
+ CustomAsciiDocDocumentBuilder.this.emitContent(c);
+ }
+
+ @Override
+ public void write(String s) throws IOException {
+ CustomAsciiDocDocumentBuilder.this.emitContent(s);
+ }
+
+ @Override
+ public void open() throws IOException {
+ super.open();
+ pushWriter(new StringWriter());
+ }
+
+ @Override
+ public void close() throws IOException {
+ Writer thisContent = popWriter();
+ String content = thisContent.toString();
+ if (content.length() > 0) {
+ emitContent(content);
+ }
+ super.close();
+ }
+
+ protected void emitContent(final String content) throws IOException {
+ CustomAsciiDocDocumentBuilder.this.emitContent(prefix);
+ String trimmedContent = content.replaceAll("\\*Note:[ ]\\*\\*", "NOTE: ");
+ // String trimmedContent = content.replaceAll("\\*[Nn][Oo][Tt][Ee]:[ ]*\\*", "NOTE: ");
+ CustomAsciiDocDocumentBuilder.this.emitContent(trimmedContent);
+ CustomAsciiDocDocumentBuilder.this.emitContent(suffix);
+ }
+
+ }
+
+ /**
+ * Handles links. Links with images are handled properly, copying images
+ * from the image directory close to the document.
+ */
+ private class LinkBlock extends AsciiDocContentBlock {
+
+ final LinkAttributes attributes;
+
+ LinkBlock(LinkAttributes attributes) {
+ super("", "", 0, 0);
+ this.attributes = attributes;
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ String href = attributes.getHref();
+ if (href == null) {
+ if (attributes.getId() != null) {
+ super.emitContent("[[" + attributes.getId() + "]]\n");
+ return;
+ }
+ LOG.log(Level.WARNING, "Empty href: {0}", href);
+ }
+ href = href == null ? "" : href;
+ href = copyImageIfRequired(href, false);
+
+ if (href.startsWith("http")) {
+ externalLinks.addExternalLink(href, CustomAsciiDocDocumentBuilder.this.relativePathToTutorialFile);
+ }
+
+ if (content.contains("image:")) {
+ // Hande links with images properly, using a image with a "link" attribute
+ // content is something like "image:images/whatever-small.png[]" (small image)
+ // href is something like "images/whatever.png" (larger image)
+ // This must be transformed (https://stackoverflow.com/questions/34299474/using-an-image-as-a-link-in-asciidoc)
+ // to
+ // image:whatever-small.png[link="whatever.png"]
+ String smallPart = content.substring(6);
+ int i = smallPart.indexOf('[');
+ smallPart = i == -1 ? smallPart : smallPart.substring(0, i);
+ StringBuilder sb = new StringBuilder();
+ sb.append("\n[.feature]\n");
+ sb.append("--\n");
+ sb.append("image");
+ sb.append(smallPart);
+ sb.append("[role=\"left\", link=\"").append(href).append("\"]\n");
+ sb.append("--\n");
+ CustomAsciiDocDocumentBuilder.this.emitContent(sb.toString());
+ } else {
+ // link::http://url.com[label]
+ CustomAsciiDocDocumentBuilder.this.emitContent("link:"); //$NON-NLS-1$
+ CustomAsciiDocDocumentBuilder.this.emitContent(href);
+ CustomAsciiDocDocumentBuilder.this.emitContent("[+");
+ if (content != null) {
+ CustomAsciiDocDocumentBuilder.this.emitContent(content);
+ }
+ CustomAsciiDocDocumentBuilder.this.emitContent("+]");
+ }
+ }
+
+ }
+
+ /**
+ * A header-1 block.
+ */
+ class AsciiDocMainHeaderBlock extends AsciiDocContentBlock {
+
+ public AsciiDocMainHeaderBlock() {
+ super("", "", 2, 2);
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ String trimmedContent = content.replaceAll("\n", " ");
+ super.emitContent("= " + trimmedContent + "\n");
+ super.emitContent(":jbake-type: platform-tutorial\n");
+ super.emitContent(":jbake-tags: tutorials \n");
+ super.emitContent(":jbake-status: published\n");
+ super.emitContent(":syntax: true\n");
+ super.emitContent(":source-highlighter: pygments\n");
+ super.emitContent(":toc: left\n");
+ super.emitContent(":toc-title:\n");
+ super.emitContent(":icons: font\n");
+ super.emitContent(":experimental:\n");
+ super.emitContent(":description: " + trimmedContent + " - Apache NetBeans\n");
+ super.emitContent(":keywords: Apache NetBeans Platform, Platform Tutorials, " + trimmedContent + "");
+ super.emitContent("\n");
+ }
+
+ }
+
+ private static final String headerPrefix(int level) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < level; i++) {
+ sb.append("=");
+ }
+ sb.append(" ");
+ return sb.toString();
+ }
+
+ class NumberedListItemBlock extends AsciiDocContentBlock {
+
+ private int count = 0;
+
+ private NumberedListItemBlock(String prefix) {
+ super(BlockType.LIST_ITEM, prefix, "", 1, 1);
+ }
+
+ @Override
+ public void open() throws IOException {
+ super.open();
+ if (getPreviousBlock() instanceof AsciiDocListBlock) {
+ AsciiDocListBlock list = (AsciiDocListBlock) getPreviousBlock();
+ list.addListItem(this);
+ count = list.getCount();
+ }
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ if (getPreviousBlock().getBlockType() == BlockType.NUMERIC_LIST) {
+ prefix = String.format("%n[start=%d]%n%d. ", count, count);
+ }
+ super.emitContent(content);
+ }
+
+ }
+
+ class AsciiDocListBlock extends AsciiDocContentBlock {
+
+ private int count = 0;
+
+ private AsciiDocListBlock(BlockType type, int leadingNewLines) {
+ super(type, "", "", leadingNewLines, 1);
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ super.emitContent(prefix);
+ super.emitContent(content);
+ if (!content.endsWith("\n\n")) {
+ super.emitContent(suffix);
+ }
+ }
+
+ protected void addListItem(NumberedListItemBlock item) {
+ count++;
+ }
+
+ protected int getCount() {
+ return count;
+ }
+ }
+
+ class AsciiDocHeaderBlock extends AsciiDocContentBlock {
+
+ private Attributes attributes;
+ private int level;
+
+ public AsciiDocHeaderBlock(int level, Attributes attributes) {
+ super("", "", 2, 2);
+ this.attributes = attributes;
+ this.level = level;
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ super.emitContent("\n");
+ if (attributes != null && attributes.getId() != null) {
+ super.emitContent("[[" + attributes.getId() + "]]\n");
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < level; i++) {
+ sb.append("=");
+ }
+ sb.append(" ");
+ sb.append(content.replaceAll("\n", " "));
+ sb.append("\n\n");
+ super.emitContent(sb.toString());
+ }
+ }
+
+ /**
+ * An inline code block.
+ */
+ class AsciiDocInlinePreformatted extends AsciiDocContentBlock {
+
+ String language;
+
+ AsciiDocInlinePreformatted(String language, CustomAsciiDocDocumentBuilder documentBuilder) {
+ super(BlockType.CODE, " ``", "`` ", 0, 0);
+ this.language = language;
+ }
+
+ }
+
+ private static final Pattern RUBY_PATTERN = Pattern.compile("^require '.*", Pattern.MULTILINE);
+ private static final Pattern C_PATTERN = Pattern.compile("^#include.*", Pattern.MULTILINE);
+ private static final Pattern SHELL_PATTERN = Pattern.compile("^\\$ ", Pattern.MULTILINE);
+
+ /**
+ * Generates a
+ * <pre> block.
+ */
+ class AsciiDocPreformatted extends AsciiDocContentBlock {
+
+ String language;
+
+ AsciiDocPreformatted(String language) {
+ super(BlockType.PREFORMATTED, "", "", 1, 1);
+ this.language = language;
+ }
+
+ @Override
+ protected void emitContent(String content) throws IOException {
+ if (language == null) {
+ // Use "java" as a default language for tutorials
+ language = "java";
+ if (content.contains("<?xml") || content.contains("</")) {
+ language = "xml";
+ }
+ if (content.contains("<div") || content.contains("<p>")) {
+ language = "html";
+ }
+ if (C_PATTERN.matcher(content).find()) {
+ language = "c";
+ }
+ if (RUBY_PATTERN.matcher(content).find()) {
+ language = "ruby";
+ }
+ if (SHELL_PATTERN.matcher(content).find()) {
+ language = "shell";
+ }
+ if (content.contains("<?php")) {
+ language = "php";
+ }
+ if (content.contains("$.") || content.contains("function (")) {
+ language = "javascript";
+ }
+ }
+ // [label](http://url.com) or
+ // [label](http://url.com "title")
+ CustomAsciiDocDocumentBuilder.this.emitContent("\n[source," + language + "]\n");
+ CustomAsciiDocDocumentBuilder.this.emitContent("----\n");
+ CustomAsciiDocDocumentBuilder.this.emitContent(content.startsWith("\n") ? content : "\n" + content);
+ CustomAsciiDocDocumentBuilder.this.emitContent("\n----\n\n");
+ }
+ }
+
+ private static Logger LOG = Logger.getLogger(CustomAsciiDocDocumentBuilder.class.getName());
+
+ private File topDirectory;
+ private File imageDirectory;
+ private File outputDirectory;
+ private File imageDestDirectory;
+ private ExternalLinksMap externalLinks;
+ private Language language;
+ private String relativePathToTutorialsRoot;
+ private File outputFile;
+ private String relativePathToTutorialFile;
+
+ public CustomAsciiDocDocumentBuilder(File topDirectory, File imageDirectory, File outputFile, BufferedWriter output, ExternalLinksMap externalLinks) {
+ super(output);
+ this.topDirectory = topDirectory;
+ this.imageDirectory = imageDirectory;
+ this.outputFile = outputFile;
+ this.outputDirectory = outputFile.getParentFile();
+ this.language = Language.getLanguage(outputFile);
+ imageDestDirectory = new File(outputDirectory, "images");
+ imageDestDirectory.mkdirs();
+ this.externalLinks = externalLinks;
+ relativePathToTutorialsRoot = outputDirectory.toURI().relativize(topDirectory.toURI()).getPath();
+ relativePathToTutorialFile = topDirectory.toURI().relativize(outputFile.toURI()).getPath();
+
+ }
+
+ /**
+ * Responsible for handling headers.
+ *
+ * @param level
+ * @param attributes
+ * @return
+ */
+ @Override
+ protected Block computeHeading(int level, Attributes attributes) {
+ if (level == 1) {
+ return new AsciiDocMainHeaderBlock();
+ }
+ //return super.computeHeading(level, attributes);
+ return new AsciiDocHeaderBlock(level, attributes);
+ }
+
+ @Override
+ protected Block computeSpan(SpanType type, Attributes attributes) {
+ switch (type) {
+ case MARK:
+ throw new IllegalStateException("Mark");
+ case MONOSPACE:
+ return new AsciiDocInlinePreformatted(null, this);
+ case LINK:
+ if (attributes instanceof LinkAttributes) {
+ LinkAttributes linkAttributes = (LinkAttributes) attributes;
+ if (linkAttributes.getHref() != null) {
+ if (linkAttributes.getHref().startsWith("#")) {
+ return new AsciiDocContentBlock("<<" + linkAttributes.getHref().substring(1) + ",", ">>", 0, 0);
+ /* This is an internal link */
+ }
+ return new LinkBlock((LinkAttributes) attributes);
+ } else if (linkAttributes.getId() != null) {
+ return new AsciiDocContentBlock("[[", "]]", 0, 1);
+ }
+ }
+ return new AsciiDocContentBlock("", "", 0, 0);
+ case DELETED:
+ return new AsciiDocContentBlock("[.line-through]#", "#", 0, 0);
+ case UNDERLINED:
+ return new AsciiDocContentBlock("[.underline]#", "#", 0, 0);
+ default:
+ return super.computeSpan(type, attributes);
+ }
+ }
+
+ @Override
+ public void link(Attributes attributes, String hrefOrHashName, String text) {
+ super.link(attributes, hrefOrHashName, text);
+ }
+
+ @Override
+ public void entityReference(String entity) {
+ super.entityReference(entity);
+ }
+
+ @Override
+ protected Block
+ computeBlock(BlockType type, Attributes attributes) {
+ switch (type) {
+ case CODE:
+ case PREFORMATTED:
+ String language = null;
+
+ if (attributes.getCssClass() != null) {
+ if (attributes.getCssClass().equals("source-java")) {
+ language = "java";
+
+ } else if (attributes.getCssClass().equals("source-xml")) {
+ language = "xml";
+
+ } else if (attributes.getCssClass().equals("source-properties")) {
+ language = "yaml";
+
+ }
+ }
+ return new AsciiDocPreformatted(language);
+ case NUMERIC_LIST:
+ if (currentBlock != null) {
+ BlockType currentBlockType = currentBlock.getBlockType();
+ if (currentBlockType == BlockType.LIST_ITEM || currentBlockType == BlockType.DEFINITION_ITEM
+ || currentBlockType == BlockType.DEFINITION_TERM) {
+ return new AsciiDocListBlock(type, 1);
+ }
+ }
+ return new AsciiDocListBlock(type, 2);
+ case LIST_ITEM:
+ if (computeCurrentListType() == BlockType.NUMERIC_LIST) {
+ return new NumberedListItemBlock(""); //$NON-NLS-1$
+ }
+ return super.computeBlock(type, attributes);
+ default:
+ return super.computeBlock(type, attributes);
+ }
+ }
+
+ @Override
+ public void image(Attributes attributes, String url) {
+ url = copyImageIfRequired(url, true);
+
+ assertOpenBlock();
+ try {
+ currentBlock.write(computeImage(attributes, url, false));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String computeImage(Attributes attributes, String url, boolean inline) {
+ // image:/path/to/img.jpg[] or
+ // image:path/to/img.jpg[alt text]
+ String altText = null;
+ String title = null;
+ if (attributes instanceof ImageAttributes) {
+ ImageAttributes imageAttr = (ImageAttributes) attributes;
+ altText = imageAttr.getAlt();
+ }
+ if (!Strings.isNullOrEmpty(attributes.getTitle())) {
+ title = "title=\"" + attributes.getTitle().replaceAll("\n", " ").replaceAll("\r", " ") + '"'; //$NON-NLS-1$
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(inline ? "image:" : "image::"); //$NON-NLS-1$
+ sb.append(Strings.nullToEmpty(url));
+ sb.append("["); //$NON-NLS-1$
+ sb.append(Joiner.on(", ").skipNulls().join(altText, title)); //$NON-NLS-1$
+ sb.append("]"); //$NON-NLS-1$
+ return sb.toString();
+ }
+
+ private String copyImageIfRequired(String url, boolean warnMissingImages) {
+ if (url.startsWith("http://") || url.startsWith("https://")) {
+ // System.err.format("Image url '%s'%n", url);
+ return url;
+ }
+ if (url.startsWith("../../images/")) {
+ url = url.replace("../../images/", "/");
+ }
+ if (url.startsWith("../images/")) {
+ url = url.replace("../images/", "/");
+ }
+ File imageFile = new File(imageDirectory, url);
+ File copiedImageFile = new File(imageDestDirectory, imageFile.getName());
+ if (imageFile.exists()) {
+ if (!copiedImageFile.exists()) {
+ try {
+ Files.copy(imageFile.toPath(), copiedImageFile.toPath());
+ url = imageDestDirectory.getName() + "/" + imageFile.getName();
+ } catch (IOException ex) {
+ Logger.getLogger(CustomAsciiDocDocumentBuilder.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ } else {
+ url = imageDestDirectory.getName() + "/" + imageFile.getName();
+ }
+ } else if (warnMissingImages) {
+ LOG.log(Level.WARNING, "Image not found: {0}\n in file {1}\n for file {2}", new Object[]{url,
+ imageFile.getAbsolutePath(),
+ CustomAsciiDocDocumentBuilder.this.outputFile.getAbsolutePath()});
+ }
+ return url;
+ }
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilderWithoutTables.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilderWithoutTables.java
new file mode 100644
index 0000000..bc701c2
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/CustomAsciiDocDocumentBuilderWithoutTables.java
@@ -0,0 +1,47 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import org.eclipse.mylyn.wikitext.parser.Attributes;
+
+/**
+ *
+ */
+public class CustomAsciiDocDocumentBuilderWithoutTables extends CustomAsciiDocDocumentBuilder {
+
+ public CustomAsciiDocDocumentBuilderWithoutTables(File topDirectory, File imageDirectory, File outputFile, BufferedWriter output, ExternalLinksMap externalLinks) {
+ super(topDirectory, imageDirectory, outputFile, output, externalLinks);
+ }
+
+ @Override
+ protected Block computeBlock(BlockType type, Attributes attributes) {
+ switch(type) {
+ case TABLE:
+ case TABLE_CELL_HEADER:
+ case TABLE_CELL_NORMAL:
+ case TABLE_ROW:
+ return new AsciiDocContentBlock("", "", 0, 0);
+ }
+ return super.computeBlock(type, attributes); //To change body of generated methods, choose Tools | Templates.
+ }
+
+
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/ExternalLinksMap.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/ExternalLinksMap.java
new file mode 100644
index 0000000..3e19585
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/ExternalLinksMap.java
@@ -0,0 +1,81 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Maps external links (String, href) to File's referencing it.
+ */
+public class ExternalLinksMap {
+
+ /** Maps "href" to tutorials. */
+ private TreeMap<String, TreeSet<String>> hrefToFile;
+ /** Maps domain names ("bits.netbeans.org", for instance) to hrefs */
+ private TreeMap<String, TreeSet<String>> domainToHref;
+
+ public ExternalLinksMap() {
+ hrefToFile = new TreeMap<>();
+ domainToHref = new TreeMap<>();
+ }
+
+ public void addExternalLink(String href, String tutorial) {
+ try {
+ URL url = new URL(href);
+ TreeSet<String> hrefs = domainToHref.get(url.getHost());
+ if (hrefs == null) {
+ hrefs = new TreeSet<>();
+ domainToHref.put(url.getHost(), hrefs);
+ }
+ hrefs.add(href);
+
+ TreeSet<String> tutorials = hrefToFile.get(href);
+ if (tutorials == null) {
+ tutorials = new TreeSet<>();
+ hrefToFile.put(href, tutorials);
+ }
+ tutorials.add(tutorial);
+ } catch (MalformedURLException ex) {
+ Logger.getLogger(ExternalLinksMap.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ public Set<String> getDomains() {
+ return domainToHref.keySet();
+ }
+
+ public Set<String> getHrefs(String domain) {
+ return domainToHref.get(domain);
+ }
+
+ public Set<String> getTutorials(String href) {
+ return hrefToFile.get(href);
+ }
+
+ @Override
+ public String toString() {
+ return domainToHref.toString() + " " + hrefToFile.toString();
+ }
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/HTMLConverter.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/HTMLConverter.java
new file mode 100644
index 0000000..9954675
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/HTMLConverter.java
@@ -0,0 +1,268 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.Transformer;
+import java.io.*;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import org.eclipse.mylyn.wikitext.parser.HtmlParser;
+import org.xml.sax.InputSource;
+
+/**
+ * Converts HTML files in a source directory to ASCIIDOC files in a destination
+ * directory.
+ *
+ * @author Antonio Vieiro <vi...@apache.org>
+ */
+public class HTMLConverter {
+
+ private static final String APACHE_LICENSE_HEADER = ""
+ + "\n"
+ + " Licensed to the Apache Software Foundation (ASF) under one\n"
+ + " or more contributor license agreements. See the NOTICE file\n"
+ + " distributed with this work for additional information\n"
+ + " regarding copyright ownership. The ASF licenses this file\n"
+ + " to you under the Apache License, Version 2.0 (the\n"
+ + " \"License\"); you may not use this file except in compliance\n"
+ + " with the License. You may obtain a copy of the License at\n"
+ + "\n"
+ + " http://www.apache.org/licenses/LICENSE-2.0\n"
+ + "\n"
+ + " Unless required by applicable law or agreed to in writing,\n"
+ + " software distributed under the License is distributed on an\n"
+ + " \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n"
+ + " KIND, either express or implied. See the License for the\n"
+ + " specific language governing permissions and limitations\n"
+ + " under the License.\n\n";
+
+ private static final Logger LOG = Logger.getLogger(HTMLConverter.class.getName());
+ private static Transformer transformer;
+ private static DocumentBuilderFactory documentBuilderFactory;
+
+ private static void convert(File docsTutorialsDocs, File docsTutorialsImages, File dest, ExternalLinksMap externalLinks) throws Exception {
+ LOG.log(Level.INFO, "Converting tutorials from {0} to {1}", new Object[]{docsTutorialsDocs.getAbsolutePath(), dest.getAbsolutePath()});
+
+ List<File> html_files = Files.find(docsTutorialsDocs.toPath(), 999,
+ (p, bfa) -> bfa.isRegularFile()).map(Path::toFile).filter((f) -> f.getName().endsWith(".html")).collect(Collectors.toList());
+
+ URI baseDirectory = docsTutorialsDocs.toURI();
+ int fileCount = 0;
+ boolean debug=false;
+ for (File htmlFile : html_files) {
+ if (debug) {
+ if (! htmlFile.getName().equals("annotations.html")) {
+ continue;
+ }
+ }
+ String relativePath = baseDirectory.relativize(htmlFile.toURI()).getPath().replaceAll("\\.html", ".asciidoc");
+ File asciidoc = new File(dest, relativePath);
+ convertHTMLToAsciiDoc(dest, htmlFile, docsTutorialsImages, asciidoc, externalLinks);
+ fileCount++;
+ }
+ LOG.log(Level.INFO, "Converted {0} tutorials.", fileCount);
+ }
+
+ private static void convertTrails(File docsTutorialsTrailsDirectory, File docsTutorialsImages, File dest, ExternalLinksMap externalLinks) throws Exception {
+ LOG.log(Level.INFO, "Converting trails {0} to {1}", new Object[]{docsTutorialsTrailsDirectory.getAbsolutePath(), dest.getAbsolutePath()});
+
+ List<File> html_files = Files.find(docsTutorialsTrailsDirectory.toPath(), 999,
+ (p, bfa) -> bfa.isRegularFile()).map(Path::toFile).filter((f) -> f.getName().endsWith(".html")).collect(Collectors.toList());
+
+ URI baseDirectory = docsTutorialsTrailsDirectory.toURI();
+ int fileCount = 0;
+ boolean debug=false;
+ for (File htmlFile : html_files) {
+ if (debug) {
+ if (! htmlFile.getName().equals("annotations.html")) {
+ continue;
+ }
+ }
+ String relativePath = baseDirectory.relativize(htmlFile.toURI()).getPath().replaceAll("\\.html", ".asciidoc");
+ File asciidoc = new File(dest, relativePath);
+ convertHTMLToAsciiDocWithoutTables(dest, htmlFile, docsTutorialsImages, asciidoc, externalLinks);
+ fileCount++;
+ }
+ LOG.log(Level.INFO, "Converted {0} trails.", fileCount);
+ }
+
+ private static String asciidocHeader = null;
+
+ private static String getAsciidocHeader() {
+ if (asciidocHeader == null) {
+ StringBuilder sb = new StringBuilder();
+ String[] lines = APACHE_LICENSE_HEADER.split("\n");
+ for (String line : lines) {
+ sb.append("// ").append(line).append("\n");
+ }
+ sb.append("//\n\n");
+ asciidocHeader = sb.toString();
+ }
+ return asciidocHeader;
+ }
+
+ private static ThreadLocal<SimpleDateFormat> sdf = null;
+
+ private static final synchronized SimpleDateFormat getSimpleDateFormat() {
+ if (sdf == null) {
+ SimpleDateFormat d = new SimpleDateFormat("yyyy-MM-dd");
+ sdf = new ThreadLocal<>();
+ sdf.set(d);
+ }
+ return sdf.get();
+ }
+
+ /**
+ * Converts the given HTML file to AsciiDoc format.
+ * @param inputHTMLFile The input file.
+ * @param imageDirectory The directory where images are to be found.
+ * @param outputAsciidocFile The output asciidoc file.
+ * @param externalLinks A map used to store external links detected in the HTML file.
+ * @throws Exception on error.
+ */
+ private static void convertHTMLToAsciiDoc(File topDirectory, File inputHTMLFile, File imageDirectory, File outputAsciidocFile, ExternalLinksMap externalLinks) throws Exception {
+ if (! outputAsciidocFile.getParentFile().exists()) {
+ if (! outputAsciidocFile.getParentFile().mkdirs()) {
+ throw new IOException(String.format("Cannot create directory '%s'", outputAsciidocFile.getParent()));
+ }
+ }
+ System.out.format("Converting '%s'%n", inputHTMLFile.getAbsolutePath());
+ try (BufferedWriter output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputAsciidocFile), "utf-8"), 16 * 1024);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputHTMLFile), "utf-8"))) {
+ output.write(getAsciidocHeader());
+
+ CustomAsciiDocDocumentBuilder asciidocBuilder = new CustomAsciiDocDocumentBuilder(topDirectory, imageDirectory, outputAsciidocFile, output, externalLinks);
+ InputSource source = new InputSource(reader);
+ HtmlParser.instance().parse(source, asciidocBuilder);
+ // HtmlParser.instanceWithHtmlCleanupRules().parse(source, asciidocBuilder);
+ }
+ }
+
+ /**
+ * Converts the given HTML file to AsciiDoc format, ignoring tables completely.
+ * @param inputHTMLFile The input file.
+ * @param imageDirectory The directory where images are to be found.
+ * @param outputAsciidocFile The output asciidoc file.
+ * @param externalLinks A map used to store external links detected in the HTML file.
+ * @throws Exception on error.
+ */
+ private static void convertHTMLToAsciiDocWithoutTables(File topDirectory, File inputHTMLFile, File imageDirectory, File outputAsciidocFile, ExternalLinksMap externalLinks) throws Exception {
+ if (! outputAsciidocFile.getParentFile().exists()) {
+ if (! outputAsciidocFile.getParentFile().mkdirs()) {
+ throw new IOException(String.format("Cannot create directory '%s'", outputAsciidocFile.getParent()));
+ }
+ }
+ try (BufferedWriter output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputAsciidocFile), "utf-8"), 16 * 1024);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputHTMLFile), "utf-8"))) {
+ output.write(getAsciidocHeader());
+
+ CustomAsciiDocDocumentBuilderWithoutTables asciidocBuilder = new CustomAsciiDocDocumentBuilderWithoutTables(topDirectory, imageDirectory, outputAsciidocFile, output, externalLinks);
+ InputSource source = new InputSource(reader);
+ HtmlParser.instance().parse(source, asciidocBuilder);
+ }
+ }
+
+ private static void checkDirectoryExists(String message, File dir) throws Exception {
+ String error = null;
+ if (!dir.exists()) {
+ error = String.format("%s '%s' does not exist.", message, dir.getAbsolutePath());
+ }
+ if (error == null && !dir.isDirectory()) {
+ error = String.format("%s '%s' is not a directory.", message, dir.getAbsolutePath());
+ }
+ if (error == null && !dir.canRead()) {
+ error = String.format("%s '%s' is not readable.", message, dir.getAbsolutePath());
+ }
+ if (error != null) {
+ throw new IllegalArgumentException(error);
+ }
+ }
+
+ private static void usage() {
+
+ System.err.println("Use: java " + HTMLConverter.class
+ .getName() + " tutorials-directory images-directory");
+ System.err.println(" See README.md for instructions on how to prepare those directories.");
+ System.exit(1);
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2) {
+ usage();
+ }
+
+ File tutorialsDirectory = new File(args[0]);
+ checkDirectoryExists("Incorrect tutorials directory ", tutorialsDirectory);
+
+ File docsTutorialsImagesDirectory = new File(args[1]);
+ checkDirectoryExists("Incorrect images directory ", docsTutorialsImagesDirectory);
+
+ File currentDirectory = new File(System.getProperty("user.dir"));
+ File dest = new File(currentDirectory, "tutorials-asciidoc");
+
+ if (!dest.exists()) {
+ if (!dest.mkdirs()) {
+ throw new IllegalStateException("Cannot create directory " + dest.getAbsolutePath());
+ }
+ }
+
+ checkDirectoryExists("Output directory", dest);
+ if (!dest.canWrite()) {
+ throw new IllegalStateException("Cannot write to " + dest.getAbsolutePath());
+ }
+
+ ExternalLinksMap externalLinks = new ExternalLinksMap();
+
+ convert(tutorialsDirectory, docsTutorialsImagesDirectory, dest, externalLinks);
+
+ convertTrails(tutorialsDirectory, docsTutorialsImagesDirectory, dest, externalLinks);
+
+ LOG.info("Generating 'external-links.txt' with list of external links...");
+
+ try ( PrintWriter ef = new PrintWriter(new FileWriter("external-links.yml"))) {
+ for (String domain : externalLinks.getDomains()) {
+ ef.format("- domain: \"%s\"%n", domain);
+ ef.format(" links:%n");
+ for (String href : externalLinks.getHrefs(domain)) {
+ ef.format(" link: \"%s\"%n", href);
+ ef.format(" used-at:%n");
+ for (String tutorial : externalLinks.getTutorials(href)) {
+ ef.format(" - \"%s\"%n", tutorial);
+ }
+ }
+ }
+ }
+
+ /* Remove a hand-made "content" section, that is not replaced by the asciidoc 'toc' stuff */
+ Map<File, String> titles = AsciidocPostProcessor.removeContentSetcion(dest);
+
+ /* Generate some "index.asciidoc" files with the list of tutorials on each directory. */
+ AsciidocPostProcessor.generateIndexes(dest, titles);
+
+ }
+
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/Language.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/Language.java
new file mode 100644
index 0000000..bf4421b
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/Language.java
@@ -0,0 +1,66 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ *
+ * @author avieiro
+ */
+public enum Language {
+ UNKNOWN("", Locale.getDefault(), "English"), DEFAULT(".asciidoc", Locale.ENGLISH, "English"), PORTUGUESE("_pt_BR.asciidoc", new Locale("pt_BR"), "Português brasileiro"), CHINESE("_zh_CN.asciidoc", new Locale("zh_CN"), "中文"), JAPANESE("_ja.asciidoc", new Locale("ja"), "日本人"), RUSSIAN("_ru.asciidoc", new Locale("ru"), "русский"), CATALAN("_ca.asciidoc", new Locale("ca_ES"), "Català");
+ public final String extension;
+ public final Locale locale;
+ public final String title;
+ /* Array of non-default languages.*/
+ public static final Language[] FOREIGN_LANGUAGES = {RUSSIAN, JAPANESE, CHINESE, PORTUGUESE, CATALAN};
+
+ Language(String extension, Locale locale, String title) {
+ this.extension = extension;
+ this.locale = locale;
+ this.title = title;
+ }
+
+ public static Language getLanguage(File file) {
+ String name = file.getName().toLowerCase();
+ for (Language language : FOREIGN_LANGUAGES) {
+ if (name.endsWith(language.extension.toLowerCase())) {
+ return language;
+ }
+ }
+ return name.endsWith(DEFAULT.extension.toLowerCase()) ? DEFAULT : UNKNOWN;
+ }
+
+ public static HashMap<Language, File> getTranslations(File file) {
+ File parentDirectory = file.getParentFile();
+ String prefix = file.getName().replace(".asciidoc", "");
+ HashMap<Language, File> translations = new HashMap<>();
+ for (Language l : FOREIGN_LANGUAGES) {
+ File translationFile = new File(parentDirectory, prefix + l.extension);
+ if (translationFile.exists()) {
+ translations.put(l, translationFile);
+ }
+ }
+ return translations;
+ }
+
+}
diff --git a/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/LocalizedTutorialSection.java b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/LocalizedTutorialSection.java
new file mode 100644
index 0000000..c58124f
--- /dev/null
+++ b/tutorials-convert/src/main/java/org/netbeans/tools/tutorials/LocalizedTutorialSection.java
@@ -0,0 +1,91 @@
+/*
+ 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.netbeans.tools.tutorials;
+
+import java.io.File;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author avieiro
+ */
+public class LocalizedTutorialSection {
+
+ public static final String URL_KEY = "url";
+ public static final String TITLE_KEY = "title";
+ private Language language;
+ private ArrayList<File> files;
+ private ArrayList<HashMap<String, String>> details;
+ private String title;
+
+ public LocalizedTutorialSection(Language language, String title) {
+ this.language = language;
+ this.files = new ArrayList<>();
+ this.details = new ArrayList<>();
+ this.title = title;
+ }
+
+ public void add(File file) {
+ this.files.add(file);
+ }
+
+ public void addAll(List<File> files) {
+ this.files.addAll(files);
+ }
+
+ public void sort(Map<File, String> fileTitles) {
+ ArrayList<File> sortedFiles = new ArrayList<>(this.files);
+ Collator collator = Collator.getInstance(language.locale);
+ sortedFiles.sort((file1, file2) -> {
+ String title1 = fileTitles.get(file1);
+ String title2 = fileTitles.get(file2);
+ title1 = title1 == null ? file1.getName() : title1;
+ title2 = title2 == null ? file2.getName() : title2;
+ return collator.compare(title1, title2);
+ });
+ this.files = sortedFiles;
+ details.clear();
+ for (File file : files) {
+ HashMap<String, String> detail = new HashMap<String, String>();
+ details.add(detail);
+ detail.put(URL_KEY, file.getName().replaceAll(".asciidoc", ".html"));
+ detail.put(TITLE_KEY, fileTitles.get(file));
+ }
+ }
+
+ public Language getLanguage() {
+ return language;
+ }
+
+ public ArrayList<File> getFiles() {
+ return files;
+ }
+
+ public ArrayList<HashMap<String, String>> getDetails() {
+ return details;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+}
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle.properties
new file mode 100644
index 0000000..ec76b64
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle.properties
@@ -0,0 +1,46 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+tutorials.title=NetBeans Platform Tutorials
+60.title=NetBeans 6.0 Platform Tutorials
+61.title=NetBeans 6.1 Platform Tutorials
+67.title=NetBeans 6.7 Platform Tutorials
+68.title=NetBeans 6.8 Platform Tutorials
+69.title=NetBeans 6.9 Platform Tutorials
+691.title=NetBeans 6.9.1 Platform Tutorials
+70.title=NetBeans 7.0 Platform Tutorials
+71.title=NetBeans 7.1 Platform Tutorials
+72.title=NetBeans 7.2 Platform Tutorials
+73.title=NetBeans 7.3 Platform Tutorials
+74.title=NetBeans 7.4 Platform Tutorials
+80.title=NetBeans 8.0 Platform Tutorials
+
+
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_es_CA.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_es_CA.properties
new file mode 100644
index 0000000..912e3ac
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_es_CA.properties
@@ -0,0 +1,31 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ja.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ja.properties
new file mode 100644
index 0000000..912e3ac
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ja.properties
@@ -0,0 +1,31 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_pt_BR.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_pt_BR.properties
new file mode 100644
index 0000000..912e3ac
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_pt_BR.properties
@@ -0,0 +1,31 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ru.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ru.properties
new file mode 100644
index 0000000..912e3ac
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_ru.properties
@@ -0,0 +1,31 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_zh_CN.properties b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_zh_CN.properties
new file mode 100644
index 0000000..912e3ac
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/TutorialsBundle_zh_CN.properties
@@ -0,0 +1,31 @@
+# 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.
+tutorials-asciidoc.title=NetBeans Tutorials
+cnd.title=C and C++ Tutorials
+ide.title=NetBeans IDE Tutorials
+java.title=Java Tutorials
+javaee.title=JavaEE Tutorials
+ecommerce.title=e-Commerce Tutorials
+javame.title=JavaME Tutorials
+php.title=PHP Tutorials
+web.title=Web Technologies Tutorials
+webclient.title=HTML5 Tutorials
+websvc.title=Web Service Tutorials
+
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/index-template.mustache b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/index-template.mustache
new file mode 100644
index 0000000..649f96e
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/index-template.mustache
@@ -0,0 +1,33 @@
+//
+// 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.
+//
+
+= {{title}}
+:jbake-type: tutorial
+:jbake-tags: tutorials
+:jbake-status: published
+:toc: left
+:toc-title:
+:description: {{title}}
+
+{{#details}}
+- link:{{url}}[{{title}}]
+{{/details}}
+
+
+
diff --git a/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/section-template.mustache b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/section-template.mustache
new file mode 100644
index 0000000..5ea7713
--- /dev/null
+++ b/tutorials-convert/src/main/resources/org/netbeans/tools/tutorials/section-template.mustache
@@ -0,0 +1,27 @@
+//
+// 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.
+//
+
+.{{title}}
+************************************************
+{{#details}}
+- link:{{url}}[{{title}}]
+{{/details}}
+************************************************
+
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists