From: Svjatoslav Agejenko Date: Sat, 21 Sep 2024 01:57:22 +0000 (+0300) Subject: initial commit X-Git-Url: http://www2.svjatoslav.eu/gitweb/?a=commitdiff_plain;h=489835de6fab48e6d728cb4f33e4d5950702d08b;p=j-org-support.git initial commit --- 489835de6fab48e6d728cb4f33e4d5950702d08b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fef95a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.settings/ +/.classpath +/.project +/target/ +/.idea/ +/*.iml diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/COPYING @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README b/README new file mode 100644 index 0000000..e8e6bc3 --- /dev/null +++ b/README @@ -0,0 +1 @@ +See "doc" directory for project documentation. diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..f51a06a --- /dev/null +++ b/doc/index.html @@ -0,0 +1,294 @@ + + + + + + + +Emacs Org Mode support for java + + + + + + + +
+

Emacs Org Mode support for java

+
+

Table of Contents

+ +
+ +
+

1 General

+
+ +
+ +
+

1.1 Source code

+
+ +
+
+
+
+

2 Current status

+
+
    +
  • work in progress
  • + +
  • Partially implemented functionality: +
      +
    • Parse Org documents into object model
    • +
    • Generate markdown documents from object model
    • +
  • +
+
+
+
+
+

Author: Svjatoslav Agejenko

+

Created: 2021-07-25 Sun 20:53

+

Validate

+
+ + diff --git a/doc/index.org b/doc/index.org new file mode 100644 index 0000000..494f797 --- /dev/null +++ b/doc/index.org @@ -0,0 +1,26 @@ +#+TITLE: Emacs Org Mode support for java + +* General +- This program is free software: released under Creative Commons Zero + (CC0) license + +- Program author: + - Svjatoslav Agejenko + - Homepage: https://svjatoslav.eu + - Email: mailto://svjatoslav@svjatoslav.eu + +- [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]] + +** Source code +- [[https://www2.svjatoslav.eu/gitweb/?p=j-org-support.git;a=snapshot;h=HEAD;sf=tgz][Download latest snapshot in TAR GZ format]] + +- [[https://www2.svjatoslav.eu/gitweb/?p=j-org-support.git;a=summary][Browse Git repository online]] + +- Clone Git repository using command: + : git clone https://www2.svjatoslav.eu/git/j-org-support.git +* Current status +- work in progress + +- Partially implemented functionality: + - Parse Org documents into object model + - Generate markdown documents from object model diff --git a/install b/install new file mode 100755 index 0000000..66a9d48 --- /dev/null +++ b/install @@ -0,0 +1,10 @@ +#!/bin/bash + +mvn clean +mvn package + +sudo mkdir /opt/j-org-support +sudo rm /opt/j-org-support/j-org-support.jar +sudo cp target/*-jar-with-dependencies.jar /opt/j-org-support/j-org-support.jar +sudo cp org2md /opt/j-org-support/ +sudo ln -s /opt/j-org-support/org2md /usr/bin/ diff --git a/org2md b/org2md new file mode 100755 index 0000000..f075b4a --- /dev/null +++ b/org2md @@ -0,0 +1,5 @@ +#!/bin/bash + +set -f + +java -Xmx4500m -jar /opt/j-org-support/j-org-support.jar "$@" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f73a95e --- /dev/null +++ b/pom.xml @@ -0,0 +1,174 @@ + + 4.0.0 + eu.svjatoslav + j-org-support + 0.1-SNAPSHOT + jar + j-org-support + Emacs Org Mode support for java + + + svjatoslav.eu + http://svjatoslav.eu + + + + UTF-8 + UTF-8 + + + + + eu.svjatoslav + svjatoslavcommons + 1.8 + + + + + + + maven-assembly-plugin + + + + eu.svjatoslav.jorgsupport.Main + + + + jar-with-dependencies + + + + + + + package-jar-with-dependencies + package + + single + + + + jar-with-dependencies + + + + eu.svjatoslav.jorgsupport.Main + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 8 + 8 + UTF-8 + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + + + foo + bar + + + + ${java.home}/bin/javadoc + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.2 + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.9.4 + + + + + + + + + org.apache.maven.wagon + wagon-ssh-external + 2.6 + + + + + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + svjatoslav.eu + svjatoslav.eu + scpexe://svjatoslav.eu:10006/srv/maven + + + + + scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/j-org-support.git + scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/j-org-support.git + HEAD + + + + + svjatoslav.eu + Svjatoslav repository + http://www2.svjatoslav.eu/maven/ + + + true + + + true + + + + + diff --git a/src/main/java/eu/svjatoslav/jorgsupport/Document.java b/src/main/java/eu/svjatoslav/jorgsupport/Document.java new file mode 100644 index 0000000..af37f8e --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/Document.java @@ -0,0 +1,122 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException; +import eu.svjatoslav.commons.string.tokenizer.Tokenizer; +import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch; +import eu.svjatoslav.jorgsupport.text.FormattedText; + +import static eu.svjatoslav.commons.string.tokenizer.Terminator.TerminationStrategy.PRESERVE; +import static eu.svjatoslav.jorgsupport.Helper.*; +import static eu.svjatoslav.jorgsupport.text.FormattedText.fromOrg; + +public class Document { + public final Heading rootHeading = new Heading( null , 0, null, null); + private Heading currentHeading = rootHeading; + + public Heading createHeading(FormattedText name, int targetLevel, String tags){ + if (currentHeading.level == (targetLevel - 1)){ + Heading newHeading = new Heading(name, targetLevel, currentHeading, tags); + currentHeading.addChild(newHeading); + currentHeading = newHeading; + return newHeading; + } + + if (currentHeading.level > (targetLevel - 1)){ + currentHeading = currentHeading.parent; + return createHeading(name, targetLevel, tags); + } + + Heading missingIntermediate = new Heading(fromOrg(""), currentHeading.level + 1, currentHeading, null); + currentHeading.addChild(missingIntermediate); + currentHeading = missingIntermediate; + return createHeading(name, targetLevel, tags); + } + + public Heading getCurrentHeading(){ + return currentHeading; + } + + private void parseHeading(TokenizerMatch token) throws InvalidSyntaxException { + String[] headingSections = token.getRegExpGroups(); + int level = headingSections[0].length(); + String title = headingSections[1]; + String tags = headingSections[2]; + createHeading(fromOrg(title), level, tags); + } + + public void parse(String fileContentsAsString) throws InvalidSyntaxException { + final Tokenizer tokenizer = new Tokenizer(fileContentsAsString); + + // Org heading: + // "*** Example Heading 1234" + tokenizer.addTerminator(PRESERVE, + "(\\*+)[ \\t](.*?)(\\:\\S*\\:)?\\r?\\n", TG_HEADING); + + // Org list. Examples: + // " + my list title" + // " - my list title" + tokenizer.addTerminator(PRESERVE, "([ \\t]*)(\\+|-)[ \\t]+(.*)?\\r?\\n", TG_LIST); + + // " * my list title" + tokenizer.addTerminator(PRESERVE, "([ \\t]+)(\\*)[ \\t]+(.*)?\\r?\\n", TG_LIST); + + // TODO: add numbered list + + // DocumentProperty: + // "#+OPTIONS: H:20 num:20" + tokenizer.addTerminator(PRESERVE, "#\\+([^\\s]+):(.*)\\r?\\n", TG_DOCUMENT_PROPERTY); + + // Drawer property: + // " :ID: 533734b9-0456-4448-9830-a43646345615" + tokenizer.addTerminator(PRESERVE, "([ \\t]*):([^\\s]+):(.*)\\r?\\n", TG_DRAWER_PROPERTY); + + + // multiline code block + tokenizer.addTerminator(PRESERVE, + "(?i)" + // ignore case + "([ \\t]*)#\\+BEGIN_SRC" + // source begin identifier + "(([ \\t]+)(.*))?(\\r?\\n)" + // source block parameters + "((?:.|\\n|\\r)*?)" + // source content + "(\\r?\\n)([ \\t]*)#\\+END_SRC(.*)\\r?\\n" // source end identifier + , TG_MULTILINE_CODE); + + // multiline export block + tokenizer.addTerminator(PRESERVE, + "(?i)" + // ignore case + "([ \\t]*)#\\+BEGIN_EXPORT" + // export begin identifier + "(([ \\t]+)(.*))?(\\r?\\n)" + // export block parameters + "((?:.|\\n|\\r)*?)" + // export content + "(\\r?\\n)([ \\t]*)#\\+END_EXPORT(.*)\\r?\\n" // export end identifier + , TG_EXPORT); + + // verse + tokenizer.addTerminator(PRESERVE, + "(?i)" + // ignore case + "([ \\t]*)#\\+BEGIN_VERSE" + // verse begin identifier + "(([ \\t]+)(.*))?(\\r?\\n)" + // verse block parameters + "((?:.|\\n|\\r)*?)" + // verse + "(\\r?\\n)([ \\t]*)#\\+END_VERSE(.*)\\r?\\n" // verse end identifier + , TG_VERSE); + + + // normal text + tokenizer.addTerminator(PRESERVE,".*\\r?\\n", TG_NORMAL_TEXT); + + while (tokenizer.hasMoreContent()) { + final TokenizerMatch tm = tokenizer.getNextToken(); + + if (tm.isGroup(TG_HEADING)){ + parseHeading(tm); + continue; + } + + currentHeading.parse(tm); + } + + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/Heading.java b/src/main/java/eu/svjatoslav/jorgsupport/Heading.java new file mode 100644 index 0000000..888e333 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/Heading.java @@ -0,0 +1,123 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import eu.svjatoslav.commons.string.String2; +import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch; +import eu.svjatoslav.jorgsupport.content.ListElement; +import eu.svjatoslav.jorgsupport.text.FormattedText; + +import java.util.ArrayList; +import java.util.List; + +import static eu.svjatoslav.jorgsupport.Helper.TG_LIST; + +public class Heading { + public final FormattedText name; + public final int level; + public final Heading parent; + public final String tags; + private final List children = new ArrayList<>(); + + public final ListElement rootListElement = new ListElement(null, -2, null, ""); + private ListElement currentListElement = rootListElement; + + public Heading(FormattedText name, int level, Heading parent, String tags){ + this.level = level; + this.name = name; + this.parent = parent; + this.tags = tags; + } + + public void addChild(Heading heading){ + children.add(heading); + } + + public List getChildren(){ + return children; + } + + public String toMD () { + StringBuilder sb = new StringBuilder(); + + // TODO: implement proper handling for multiple tags. For instance this is also possible: + // :noexport:projectx:important: + if (":noexport:".equalsIgnoreCase(tags)) + return ""; + + if (level > 0) sb.append(enlistTitleInMD()); + + rootListElement.toMD(sb, -2); + + children.stream().map(Heading::toMD).forEach(sb::append); + + return sb.toString(); + } + + private String enlistTitleInMD() { + String2 s = new String2(); + s.append("#", level).append(" ").append(name.toMD(0)).append("\n"); + return s.toString(); + } + + public ListElement getCurrentHeading(){ + return currentListElement; + } + + public void parse(TokenizerMatch tm){ + + int indent = Utils.getLineIndent(tm.token); + if (indent > -1 && indent <= currentListElement.indent) handleListDepthDecrease(indent); + + if (tm.isGroup(TG_LIST)){ + parseList(tm); + return; + } + + currentListElement.parse(tm); + } + + private void handleListDepthDecrease(int indent) { + while (true){ + if (currentListElement.parent.indent <= indent){ + currentListElement = currentListElement.parent; + return; + } + currentListElement = currentListElement.parent; + } + } + + private void parseList(TokenizerMatch tm) { + String[] listSections = tm.getRegExpGroups(); + int indent = listSections[0].length(); + String type = listSections[1]; + + String title = getPartialTitle(listSections); + + if (indent > currentListElement.indent){ + // list dept increases + ListElement newElement = new ListElement(title, indent, currentListElement, type); + currentListElement.addContent(newElement); + currentListElement = newElement; + return; + } + + if (indent > currentListElement.parent.indent){ + // list depth is the same + ListElement newElement = new ListElement(title, indent, currentListElement.parent, type); + currentListElement.parent.addContent(newElement); + currentListElement = newElement; + return; + } + + throw new RuntimeException("Impossible condition reached. Must be a bug!"); + } + + private String getPartialTitle(String[] listSections) { + return listSections.length > 2 ? listSections[2] : ""; + } + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/Helper.java b/src/main/java/eu/svjatoslav/jorgsupport/Helper.java new file mode 100644 index 0000000..cac11a6 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/Helper.java @@ -0,0 +1,40 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +public class Helper { + + public static final String TG_NORMAL_TEXT = "normaltext"; + + /** + * Example: + * + * #+BEGIN_SRC java + * public interface MyInterface + * { + * Integer myMethod ( String inputArgument ); + * } + * #+END_SRC + */ + public static final String TG_MULTILINE_CODE = "multiline code"; + + /** + * Example: + * + * #+begin_export latex + * \clearpage + * #+end_export + */ + public static final String TG_EXPORT = "multiline export"; + public static final String TG_VERSE = "verse"; + public static final String TG_HYPERLINK = "hyperlink"; + public static final String TG_HEADING = "heading"; + public static final String TG_LIST = "list"; + + public static final String TG_DOCUMENT_PROPERTY = "document property"; + public static final String TG_DRAWER_PROPERTY = "drawer property"; + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/Main.java b/src/main/java/eu/svjatoslav/jorgsupport/Main.java new file mode 100755 index 0000000..bff0f75 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/Main.java @@ -0,0 +1,68 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import eu.svjatoslav.commons.commandline.parameterparser.Parser; +import eu.svjatoslav.commons.commandline.parameterparser.parameter.FileParameter; +import eu.svjatoslav.commons.commandline.parameterparser.parameter.NullParameter; +import eu.svjatoslav.commons.file.FilePathParser; +import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException; + +import java.io.File; +import java.io.IOException; + +class Main { + + final static Parser parser = new Parser(); + + private static final FileParameter inputFileParameter = parser.add( + new FileParameter("Input ORG file").mustExist().setMandatory().addAliases("-i")); + + private static final FileParameter outputFileParameter = parser.add( + new FileParameter("Output MD file").setMandatory().addAliases("-o")); + + private static final NullParameter helpParameter = parser.add( + new NullParameter("Show help").addAliases("--help")); + + + private static File getOutputFileBasedOnInputFile(File input) { + File parentDirectory = input.getParentFile(); + String outputFileName = FilePathParser.getFileNameWithoutExtension(input) + ".md"; + return new File(parentDirectory, outputFileName); + } + + public static void main(final String[] args) throws Exception { + + if (!parser.parse(args)) { + parser.showHelp(); + return; + } + + if (helpParameter.isSpecified()) parser.showHelp(); + + if (!inputFileParameter.isSpecified()) return; + + orgToMd(inputFileParameter.getValue(), getOutputFile()); + } + + private static File getOutputFile() { + if (outputFileParameter.isSpecified()) + return outputFileParameter.getValue(); + + return getOutputFileBasedOnInputFile(inputFileParameter.getValue()); + } + + private static void orgToMd(File inputFile, File outputFile) throws IOException, InvalidSyntaxException { + // parse input file into object model + OrgParser orgParser = new OrgParser(); + Document document = orgParser.parse(inputFile ); + + // synthesize Markdown file from object model + MdGenerator generator = new MdGenerator(); + generator.generate(document, outputFile); + } + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/MdGenerator.java b/src/main/java/eu/svjatoslav/jorgsupport/MdGenerator.java new file mode 100644 index 0000000..866c7cc --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/MdGenerator.java @@ -0,0 +1,26 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import java.io.File; +import java.io.IOException; + +import static eu.svjatoslav.commons.file.IOHelper.saveToFile; + +public class MdGenerator { + + private StringBuilder sb; + + public void generate(Document document, File file) throws IOException { + sb = new StringBuilder(); + + sb.append(document.rootHeading.toMD()); + + saveToFile(file, sb.toString()); + } + + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/OrgParser.java b/src/main/java/eu/svjatoslav/jorgsupport/OrgParser.java new file mode 100644 index 0000000..a88fc52 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/OrgParser.java @@ -0,0 +1,29 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import eu.svjatoslav.commons.string.tokenizer.InvalidSyntaxException; + +import java.io.File; +import java.io.IOException; + +import static eu.svjatoslav.commons.file.IOHelper.getFileContentsAsString; + +public class OrgParser { + + private Document document; + + public Document parse(File file) throws IOException, InvalidSyntaxException { + document = new Document(); + + String fileContentsAsString = getFileContentsAsString(file); + + document.parse(fileContentsAsString); + + return document; + } + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/Utils.java b/src/main/java/eu/svjatoslav/jorgsupport/Utils.java new file mode 100644 index 0000000..c693408 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/Utils.java @@ -0,0 +1,61 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport; + +import eu.svjatoslav.commons.string.String2; + +public class Utils { + + public static final char[] whitespace = new char[]{'\n', '\r', ' ', '\t'}; + + public static String addIndentExceptFirstLine(String input, int indent) { + String[] lines = input.split("\\r?\\n"); + + StringBuilder sb = new StringBuilder(); + + if (lines.length >0 ) sb.append(lines[0]); + + for (int i = 1; i< lines.length; i++) { + sb.append("\n"); + sb.append(new String2(" ").repeat(indent).toString()); + sb.append(lines[i]); + } + + return sb.toString(); + } + + public static boolean isBlank(String s){ + for (char c : s.toCharArray()) + if (!isWhitespaceChar(c)) return false; + + return true; + } + + /** + * @return line indent in characters or -1 if line is blank or empty + */ + public static int getLineIndent(String line){ + for (int i = 0; i < line.length(); i++) { + if (!isWhitespaceChar(line.charAt(i))) + return i; + } + return -1; + } + + public static boolean isWhitespaceChar(char c){ + for (char whitespaceChar : whitespace) + if (whitespaceChar == c) return true; + + return false; + } + + public static String removePrefix(String string, int charsToRemove){ + String2 s = new String2(string); + s.trimPrefix(charsToRemove); + return s.toString(); + } + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/Content.java b/src/main/java/eu/svjatoslav/jorgsupport/content/Content.java new file mode 100644 index 0000000..437a5b9 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/Content.java @@ -0,0 +1,10 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +public interface Content { + void toMD(StringBuilder sb, int indent); +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/DocumentPropertyCollection.java b/src/main/java/eu/svjatoslav/jorgsupport/content/DocumentPropertyCollection.java new file mode 100644 index 0000000..d46668b --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/DocumentPropertyCollection.java @@ -0,0 +1,45 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Things like: + * #+RESULTS: + * #+LATEX_HEADER: \\usepackage{parskip} + * #+OPTIONS: H:20 num:20 + * #+attr_latex: :width 300px + */ + +public class DocumentPropertyCollection implements Content { + private Map> keyToValue = new HashMap<>(); + + @Override + public void toMD(StringBuilder sb, int indent) { + } + + public void addProperty(String key, String value){ + getOrCreateValueList(key).add(value); + } + + private List getOrCreateValueList(String key){ + String actualKey = key.toLowerCase(); + if (keyToValue.containsKey(actualKey)) + return keyToValue.get(actualKey); + + List valueList = new ArrayList(); + keyToValue.put(actualKey, valueList); + return valueList; + } + + public boolean hasProperty(String key){ + return keyToValue.containsKey(key.toLowerCase()); + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/ListElement.java b/src/main/java/eu/svjatoslav/jorgsupport/content/ListElement.java new file mode 100644 index 0000000..142bc1f --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/ListElement.java @@ -0,0 +1,173 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +import eu.svjatoslav.commons.string.String2; +import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch; +import eu.svjatoslav.jorgsupport.text.FormattedText; + +import java.util.ArrayList; +import java.util.List; + +import static eu.svjatoslav.jorgsupport.Helper.*; +import static eu.svjatoslav.jorgsupport.Utils.isBlank; +import static eu.svjatoslav.jorgsupport.Utils.removePrefix; + +public class ListElement implements Content { + StringBuilder nameInOrg = new StringBuilder(); + public final int indent; + public final ListElement parent; + private final String type; + private final List content = new ArrayList<>(); + + public ListElement(String nameInOrg, int indent, ListElement parent, String type) { + this.indent = indent; + this.nameInOrg.append(nameInOrg); + this.type = type; + this.parent = parent; + } + + private Content findCurrentContentElement(){ + if (content.isEmpty()) return null; + return content.get(content.size()-1); + } + + private boolean isLastContentElement(Class aClass){ + Content contentElement = findCurrentContentElement(); + if (contentElement == null) return false; + + return aClass.isInstance(contentElement); + } + + public void addContent(Content contentElement){ + content.add(contentElement); + } + + public void parse(TokenizerMatch tm) { + + if (tm.isGroup(TG_DOCUMENT_PROPERTY)) { + parseDocumentProperty(tm); + return; + } + + if (tm.isGroup(TG_DRAWER_PROPERTY)) { + // TODO + // System.out.println("DOCUMENT PROPERTY!!!: " + tm.token); + return; + } + + if (tm.isGroup(TG_NORMAL_TEXT)) { + if (isBlank(tm.token)){ + parseSeparator(); + return; + } + + parseTextBlock(tm); + return; + } + + if (tm.isGroup(TG_MULTILINE_CODE)){ + // System.out.println(tm.toString()); + String[] groups = tm.getRegExpGroups(); + content.add(new MultilineCode( + groups[3], // language + groups[5] // code + )); + return; + } + + if (tm.isGroup(TG_EXPORT)){ + // export is ignored + return; + } + + if (tm.isGroup(TG_VERSE)){ + String[] groups = tm.getRegExpGroups(); + content.add(new Verse(groups[5])); + return; + } + + System.out.println("ERROR!!!! Unable to handle: " + tm); + } + + private void parseTextBlock(TokenizerMatch tm) { + TextBlock textBlock; + if (isLastContentElement(TextBlock.class)) { + textBlock = ((TextBlock) findCurrentContentElement()); + } if (content.isEmpty()){ + // list title continuation + nameInOrg.append("\n").append(removePrefix(tm.token, indent + 2)); + return; + } else { + textBlock = new TextBlock(); + content.add(textBlock); + } + + textBlock.addContent(tm.token); + } + + private void parseDocumentProperty(TokenizerMatch tm) { + DocumentPropertyCollection documentPropertyCollection; + + if (isLastContentElement(DocumentPropertyCollection.class)){ + documentPropertyCollection = (DocumentPropertyCollection)findCurrentContentElement(); + } else { + documentPropertyCollection = new DocumentPropertyCollection(); + content.add(documentPropertyCollection); + } + + documentPropertyCollection.addProperty( + tm.getRegExpGroups()[0], + tm.getRegExpGroups()[1]); + } + + private void parseSeparator() { + if (isLastContentElement(Separator.class)){ + ((Separator)findCurrentContentElement()).addLine(); + } else { + content.add(new Separator()); + } + } + + + public void toMD(StringBuilder sb, int indent) { + disablePlantUmlExport(); + + if (this.indent >= 0) { + String2 s = new String2(); + s.append(" ", indent).append(type).append(" ").append(getName().toMD(indent + 2)).append("\n"); + sb.append(s.toString()); + } + + for (Content c : content) { + c.toMD(sb, this.indent + 2); + } + } + + private FormattedText getName(){ + return FormattedText.fromOrg(nameInOrg.toString()); + } + + private void disablePlantUmlExport() { + + for (int i = 0; i< (content.size()-2); i++){ + if (!(content.get(i) instanceof MultilineCode)) continue; + + MultilineCode code = (MultilineCode) content.get(i); + if (!"plantuml".equalsIgnoreCase(code.language)) continue; + + if (!(content.get(i+1) instanceof DocumentPropertyCollection)) continue; + DocumentPropertyCollection property = (DocumentPropertyCollection) content.get(i+1); + + if (!property.hasProperty("results")) continue; + + if (!(content.get(i+2) instanceof TextBlock)) continue; + TextBlock textBlock = (TextBlock) content.get(i+2); + + textBlock.disableForExport(); + } + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/MultilineCode.java b/src/main/java/eu/svjatoslav/jorgsupport/content/MultilineCode.java new file mode 100644 index 0000000..601cd91 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/MultilineCode.java @@ -0,0 +1,44 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +import eu.svjatoslav.commons.string.String2; +import eu.svjatoslav.commons.string.tokenizer.Tokenizer; + +import static eu.svjatoslav.commons.string.tokenizer.Terminator.TerminationStrategy.DROP; + +public class MultilineCode implements Content { + public final String language; + public final String code; + + public MultilineCode(String languageAndParams, String code) { + + final Tokenizer tokenizer = new Tokenizer(languageAndParams); + tokenizer.addTerminator(DROP, "[ \\t]+", "whitespace"); + if (tokenizer.hasMoreContent()){ + language = tokenizer.getNextToken().token; + } else { + language = null; + } + + this.code = code; + } + + @Override + public void toMD(StringBuilder sb, int indent) { + String2 s = new String2(); + s.append(" ", indent).append("```" + getMDlanguage() + "\n"); + // TODO: ensure that required indent is present + s.append(code + "\n"); + s.append(" ", indent).append("```\n"); + sb.append(s.toString()); + } + + public String getMDlanguage(){ + if (language == null) return ""; + return language; + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/Separator.java b/src/main/java/eu/svjatoslav/jorgsupport/content/Separator.java new file mode 100644 index 0000000..78e1f4f --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/Separator.java @@ -0,0 +1,25 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +public class Separator implements Content { + + private int numLines = 1; + + public Separator(){ + } + + @Override + public void toMD(StringBuilder sb, int indent) { + for (int i = 0; i < numLines; i++) { + sb.append("\n"); + } + } + + public void addLine(){ + numLines++; + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/TextBlock.java b/src/main/java/eu/svjatoslav/jorgsupport/content/TextBlock.java new file mode 100644 index 0000000..e9f9d59 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/TextBlock.java @@ -0,0 +1,32 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +import eu.svjatoslav.jorgsupport.text.FormattedText; + +public class TextBlock implements Content { + private StringBuilder orgAccumulator = new StringBuilder(); + private boolean enabledForExport = true; + + public TextBlock (){ + } + + public void addContent(String content){ + orgAccumulator.append(content); + } + + @Override + public void toMD(StringBuilder sb, int indent) { + if (!enabledForExport) return; + + FormattedText text = FormattedText.fromOrg(orgAccumulator.toString()); + sb.append(text.toMD(indent) + "\n"); + } + + public void disableForExport(){ + enabledForExport = false; + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/content/Verse.java b/src/main/java/eu/svjatoslav/jorgsupport/content/Verse.java new file mode 100644 index 0000000..4c6a3cc --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/content/Verse.java @@ -0,0 +1,26 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.content; + +import eu.svjatoslav.commons.string.String2; + +public class Verse implements Content { + public final String verse; + + public Verse(String verse) { + this.verse = verse; + } + + @Override + public void toMD(StringBuilder sb, int indent) { + String2 s = new String2(); + s.append(" ", indent).append("```\n"); + // TODO: ensure that required indent is present + s.append(verse + "\n"); + s.append(" ", indent).append("```\n"); + sb.append(s.toString()); + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedText.java b/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedText.java new file mode 100644 index 0000000..80f0a81 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedText.java @@ -0,0 +1,63 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.text; + +import eu.svjatoslav.commons.string.tokenizer.Tokenizer; +import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch; + +import java.util.ArrayList; +import java.util.List; + +import static eu.svjatoslav.jorgsupport.Helper.TG_HYPERLINK; +import static eu.svjatoslav.jorgsupport.Utils.addIndentExceptFirstLine; + +public class FormattedText { + List elements = new ArrayList<>(); + + public void parseOrgSyntax(String orgText) { + + Tokenizer tokenizer = getTokenizer(orgText); + while (tokenizer.hasMoreContent()) { + final TokenizerMatch token = tokenizer.getNextToken(); + + if (token.isGroup(TG_HYPERLINK)){ + elements.add(Hyperlink.fromOrg(token)); + continue; + } + + PlainText plainText = new PlainText(token.token); + elements.add(plainText); + } + + } + + public static FormattedText fromOrg(String orgText){ + FormattedText formattedText = new FormattedText(); + formattedText.parseOrgSyntax(orgText); + return formattedText; + } + + public String toMD(int indent){ + StringBuilder sb = new StringBuilder(); + + for (FormattedTextElement element : elements) + sb.append(element.toMD()); + + return addIndentExceptFirstLine(sb.toString(), indent); + } + + private Tokenizer getTokenizer(String contents) { + final Tokenizer tokenizer = new Tokenizer(contents); + tokenizer.addTerminator(Hyperlink.orgTerminator); + tokenizer.addTerminator(Hyperlink.orgTerminator2); + return tokenizer; + } + + + public String toString(){ + return toMD(0); + } +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedTextElement.java b/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedTextElement.java new file mode 100644 index 0000000..2c5ac6b --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/text/FormattedTextElement.java @@ -0,0 +1,11 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.text; + +public interface FormattedTextElement { + String toMD(); +} + diff --git a/src/main/java/eu/svjatoslav/jorgsupport/text/Hyperlink.java b/src/main/java/eu/svjatoslav/jorgsupport/text/Hyperlink.java new file mode 100644 index 0000000..6145e83 --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/text/Hyperlink.java @@ -0,0 +1,49 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.text; + +import eu.svjatoslav.commons.string.tokenizer.Terminator; +import eu.svjatoslav.commons.string.tokenizer.TokenizerMatch; + +import static eu.svjatoslav.commons.string.tokenizer.Terminator.TerminationStrategy.PRESERVE; +import static eu.svjatoslav.jorgsupport.Helper.TG_HYPERLINK; + +public class Hyperlink implements FormattedTextElement { + + public static final Terminator orgTerminator = + new Terminator(PRESERVE, "\\[\\[([\\s\\S]+)\\][ \\t\\r\\n]*\\[([\\s\\S]+)\\]\\]", TG_HYPERLINK); + + public static final Terminator orgTerminator2 = + new Terminator(PRESERVE, "\\[\\[([\\s\\S]+)\\]\\]", TG_HYPERLINK); + + private String label; + private String URL; + + @Override + public String toMD() { + + if (URL.startsWith("id:")) + return label; // TODO + + return "[" + label + "]("+ URL + ")"; + } + + public static Hyperlink fromOrg(TokenizerMatch tokenizerMatch) { + Hyperlink hyperlink = new Hyperlink(); + hyperlink.parseOrg(tokenizerMatch); + return hyperlink; + } + + private void parseOrg(TokenizerMatch tokenizerMatch) { + String[] regExpGroups = tokenizerMatch.getRegExpGroups(); + URL = regExpGroups[0]; + if (tokenizerMatch.terminator == orgTerminator){ + label = regExpGroups[1]; + } + + } + +} diff --git a/src/main/java/eu/svjatoslav/jorgsupport/text/PlainText.java b/src/main/java/eu/svjatoslav/jorgsupport/text/PlainText.java new file mode 100644 index 0000000..f1542cd --- /dev/null +++ b/src/main/java/eu/svjatoslav/jorgsupport/text/PlainText.java @@ -0,0 +1,22 @@ +/* + * j-org-support - Emacs Org Mode support for java. Author: Svjatoslav Agejenko. + * This project is released under Creative Commons Zero (CC0) license. + */ + +package eu.svjatoslav.jorgsupport.text; + +public class PlainText implements FormattedTextElement { + + private String content; + + public PlainText(String content){ + this.content = content; + } + + @Override + public String toMD() { + return content; + } +} + + diff --git a/tools/open with IntelliJ IDEA b/tools/open with IntelliJ IDEA new file mode 100755 index 0000000..de9bae5 --- /dev/null +++ b/tools/open with IntelliJ IDEA @@ -0,0 +1,18 @@ +#!/bin/bash + +# +# This is a helper bash script that starts IntelliJ with the current project. +# Script is written is such a way that you can simply click on it in file +# navigator to run it. +# +# +# Script assumes: +# +# + GNU operating system +# + IntelliJ is installed and commandline launcher "idea" is enabled. +# + +cd "${0%/*}" +cd .. + +setsid idea . &>/dev/null