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 <vie...@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 <vie...@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 <vie...@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-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists