mirror of
https://github.com/lucaspalomodevelop/txtmark.git
synced 2026-03-12 23:37:22 +00:00
Initial commit.
This commit is contained in:
commit
6543a1a55b
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
build/
|
||||
doc/
|
||||
release/
|
||||
.settings
|
||||
.classpath
|
||||
.project
|
||||
165
LICENSE.txt
Normal file
165
LICENSE.txt
Normal file
@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
8
README
Normal file
8
README
Normal file
@ -0,0 +1,8 @@
|
||||
txtmark - Java markdown processor
|
||||
Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
See LICENSE.txt for licensing information.
|
||||
-------------------------------------------------------
|
||||
|
||||
|
||||
---
|
||||
Project link: https://github.com/rjeschke/txtmark
|
||||
44
build.xml
Normal file
44
build.xml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="txtmark" default="main">
|
||||
|
||||
<target name="clean" description="Cleans the build folder">
|
||||
<delete dir="build"/>
|
||||
</target>
|
||||
|
||||
<target name="clean_all" depends="clean" description="Cleans the build, doc and release folder">
|
||||
<delete dir="doc"/>
|
||||
<delete dir="release"/>
|
||||
</target>
|
||||
|
||||
<target name="compile" description="Compiles the Java sources">
|
||||
<mkdir dir="build/classes"/>
|
||||
<javac srcdir="src/java" destdir="build/classes" target="1.6" includeAntRuntime="false"/>
|
||||
</target>
|
||||
|
||||
<target name="doc" description="Generates the JavaDoc">
|
||||
<mkdir dir="doc"/>
|
||||
<javadoc
|
||||
sourcepath="src/java"
|
||||
access="public"
|
||||
author="true"
|
||||
destdir="doc"
|
||||
encoding="UTF-8"
|
||||
charset="UTF-8"
|
||||
link="http://download.oracle.com/javase/6/docs/api/"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile" description="Creates the binary release inside build/jar">
|
||||
<mkdir dir="build/jar"/>
|
||||
<jar destfile="build/jar/txtmark.jar" basedir="build/classes"/>
|
||||
</target>
|
||||
|
||||
<target name="main" depends="clean,jar" description="Executes clean, compile and jar"/>
|
||||
|
||||
<target name="release" depends="clean_all,jar,doc" description="Builds the release version">
|
||||
<mkdir dir="release"/>
|
||||
<copy file="build/jar/txtmark.jar" toFile="release/txtmark.jar"/>
|
||||
<jar destfile="release/txtmark_javadoc.jar" basedir="doc"/>
|
||||
<jar destfile="release/txtmark_src.jar" basedir="src/java"/>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
187
src/java/txtmark/Block.java
Normal file
187
src/java/txtmark/Block.java
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
class Block
|
||||
{
|
||||
public BlockType type = BlockType.NONE;
|
||||
public Line lines = null, lineTail = null;
|
||||
public Block blocks = null, blockTail = null, blockParent = null;
|
||||
public Block next = null, previous = null;
|
||||
public int hlDepth = 0;
|
||||
|
||||
public Block()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public boolean hasLines()
|
||||
{
|
||||
return this.lines != null;
|
||||
}
|
||||
|
||||
public void removeSurroundingEmptyLines()
|
||||
{
|
||||
if(this.lines != null)
|
||||
{
|
||||
this.removeTrailingEmptyLines();
|
||||
this.removeLeadingEmptyLines();
|
||||
}
|
||||
}
|
||||
|
||||
public void transfromHeadline()
|
||||
{
|
||||
if(this.hlDepth > 0)
|
||||
return;
|
||||
int level = 0;
|
||||
final Line line = this.lines;
|
||||
if(line.isEmpty)
|
||||
return;
|
||||
int start = line.leading;
|
||||
while(start < line.value.length() && line.value.charAt(start) == '#')
|
||||
{
|
||||
level++;
|
||||
start++;
|
||||
}
|
||||
while(start < line.value.length() && line.value.charAt(start) == ' ')
|
||||
start++;
|
||||
if(start >= line.value.length())
|
||||
{
|
||||
line.setEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
int end = line.value.length() - line.trailing - 1;
|
||||
while(line.value.charAt(end) == '#')
|
||||
end--;
|
||||
while(line.value.charAt(end) == ' ')
|
||||
end--;
|
||||
line.value = line.value.substring(start, end + 1);
|
||||
line.leading = line.trailing = 0;
|
||||
}
|
||||
this.hlDepth = level;
|
||||
}
|
||||
|
||||
public void removeListIndent()
|
||||
{
|
||||
Line line = this.lines;
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty)
|
||||
{
|
||||
switch(line.getLineType())
|
||||
{
|
||||
case ULIST:
|
||||
line.value = line.value.substring(line.leading + 2);
|
||||
break;
|
||||
case OLIST:
|
||||
line.value = line.value.substring(line.value.indexOf('.') + 2);
|
||||
break;
|
||||
default:
|
||||
line.value = line.value.substring(Math.min(line.leading, 4));
|
||||
break;
|
||||
}
|
||||
line.initLeading();
|
||||
}
|
||||
line = line.next;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeBlockQuotePrefix()
|
||||
{
|
||||
Line line = this.lines;
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty)
|
||||
{
|
||||
if(line.value.charAt(line.leading) == '>')
|
||||
{
|
||||
int rem = line.leading + 1;
|
||||
if(line.leading + 1 < line.value.length() && line.value.charAt(line.leading + 1) == ' ')
|
||||
rem++;
|
||||
line.value = line.value.substring(rem);
|
||||
line.initLeading();
|
||||
}
|
||||
}
|
||||
line = line.next;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeLeadingEmptyLines()
|
||||
{
|
||||
boolean wasEmpty = false;
|
||||
Line line = this.lines;
|
||||
while(line != null && line.isEmpty)
|
||||
{
|
||||
this.removeLine(line);
|
||||
line = this.lines;
|
||||
wasEmpty = true;
|
||||
}
|
||||
return wasEmpty;
|
||||
}
|
||||
|
||||
public void removeTrailingEmptyLines()
|
||||
{
|
||||
Line line = this.lineTail;
|
||||
while(line != null && line.isEmpty)
|
||||
{
|
||||
this.removeLine(line);
|
||||
line = this.lineTail;
|
||||
}
|
||||
}
|
||||
|
||||
public Block split(final Line line)
|
||||
{
|
||||
final Block block = new Block();
|
||||
|
||||
block.lines = this.lines;
|
||||
block.lineTail = line;
|
||||
this.lines = line.next;
|
||||
line.next = null;
|
||||
if(this.lines == null)
|
||||
this.lineTail = null;
|
||||
else
|
||||
this.lines.previous = null;
|
||||
|
||||
block.blockParent = this;
|
||||
if(this.blocks == null)
|
||||
this.blocks = this.blockTail = block;
|
||||
else
|
||||
{
|
||||
block.previous = this.blockTail;
|
||||
this.blockTail.next = block;
|
||||
this.blockTail = block;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
public void removeLine(final Line line)
|
||||
{
|
||||
if(line.previous == null)
|
||||
this.lines = line.next;
|
||||
else
|
||||
line.previous.next = line.next;
|
||||
if(line.next == null)
|
||||
this.lineTail = line.previous;
|
||||
else
|
||||
line.next.previous = line.previous;
|
||||
line.previous = line.next = null;
|
||||
}
|
||||
|
||||
public void appendLine(final Line line)
|
||||
{
|
||||
if(this.lineTail == null)
|
||||
this.lines = this.lineTail = line;
|
||||
else
|
||||
{
|
||||
this.lineTail.nextEmpty = line.isEmpty;
|
||||
line.prevEmpty = this.lineTail.isEmpty;
|
||||
line.previous = this.lineTail;
|
||||
this.lineTail.next = line;
|
||||
this.lineTail = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/java/txtmark/BlockType.java
Normal file
19
src/java/txtmark/BlockType.java
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
enum BlockType
|
||||
{
|
||||
NONE,
|
||||
BLOCKQUOTE,
|
||||
CODE,
|
||||
HEADLINE,
|
||||
LIST_ITEM,
|
||||
ORDERED_LIST,
|
||||
PARAGRAPH,
|
||||
RULER,
|
||||
UNORDERED_LIST,
|
||||
XML
|
||||
}
|
||||
537
src/java/txtmark/Emitter.java
Normal file
537
src/java/txtmark/Emitter.java
Normal file
@ -0,0 +1,537 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
class Emitter
|
||||
{
|
||||
private final HashMap<String, LinkRef> linkRefs = new HashMap<String, LinkRef>();
|
||||
|
||||
public Emitter()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public void addLinkRef(final String key, final LinkRef linkRef)
|
||||
{
|
||||
this.linkRefs.put(key.toLowerCase(), linkRef);
|
||||
}
|
||||
|
||||
public void emit(final StringBuilder out, final Block root)
|
||||
{
|
||||
root.removeSurroundingEmptyLines();
|
||||
|
||||
switch(root.type)
|
||||
{
|
||||
case RULER:
|
||||
out.append("<hr />");
|
||||
return;
|
||||
case NONE:
|
||||
case XML:
|
||||
break;
|
||||
case HEADLINE:
|
||||
out.append("<h");
|
||||
out.append(root.hlDepth);
|
||||
out.append('>');
|
||||
break;
|
||||
case PARAGRAPH:
|
||||
out.append("<p>");
|
||||
break;
|
||||
case CODE:
|
||||
out.append("<pre><code>");
|
||||
break;
|
||||
case BLOCKQUOTE:
|
||||
out.append("<blockquote>");
|
||||
break;
|
||||
case UNORDERED_LIST:
|
||||
out.append("<ul>\n");
|
||||
break;
|
||||
case ORDERED_LIST:
|
||||
out.append("<ol>\n");
|
||||
break;
|
||||
case LIST_ITEM:
|
||||
out.append("<li>");
|
||||
break;
|
||||
}
|
||||
|
||||
if(root.hasLines())
|
||||
{
|
||||
this.emitLines(out, root);
|
||||
}
|
||||
else
|
||||
{
|
||||
Block block = root.blocks;
|
||||
while(block != null)
|
||||
{
|
||||
this.emit(out, block);
|
||||
block = block.next;
|
||||
}
|
||||
}
|
||||
|
||||
switch(root.type)
|
||||
{
|
||||
case RULER:
|
||||
case NONE:
|
||||
case XML:
|
||||
break;
|
||||
case HEADLINE:
|
||||
out.append("</h");
|
||||
out.append(root.hlDepth);
|
||||
out.append(">\n");
|
||||
break;
|
||||
case PARAGRAPH:
|
||||
out.append("</p>\n");
|
||||
break;
|
||||
case CODE:
|
||||
out.append("</code></pre>\n");
|
||||
break;
|
||||
case BLOCKQUOTE:
|
||||
out.append("</blockquote>\n");
|
||||
break;
|
||||
case UNORDERED_LIST:
|
||||
out.append("</ul>\n");
|
||||
break;
|
||||
case ORDERED_LIST:
|
||||
out.append("</ol>\n");
|
||||
break;
|
||||
case LIST_ITEM:
|
||||
out.append("</li>\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void emitLines(final StringBuilder out, final Block block)
|
||||
{
|
||||
switch(block.type)
|
||||
{
|
||||
case CODE:
|
||||
this.emitCodeLines(out, block.lines);
|
||||
break;
|
||||
case XML:
|
||||
this.emitRawLines(out, block.lines);
|
||||
break;
|
||||
default:
|
||||
this.emitMarkedLines(out, block.lines);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCode(final StringBuilder out, final String in, int start, int end)
|
||||
{
|
||||
for(int i = start; i < end; i++)
|
||||
{
|
||||
final char c;
|
||||
switch(c = in.charAt(i))
|
||||
{
|
||||
case '&':
|
||||
out.append("&");
|
||||
break;
|
||||
case '<':
|
||||
out.append("<");
|
||||
break;
|
||||
case '>':
|
||||
out.append(">");
|
||||
break;
|
||||
default:
|
||||
out.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int findToken(final String in, int start, MarkToken token)
|
||||
{
|
||||
int pos = start;
|
||||
while(pos < in.length())
|
||||
{
|
||||
if(this.getToken(in, pos) == token)
|
||||
return pos;
|
||||
pos++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int skipSpaces(final String in, int start)
|
||||
{
|
||||
int pos = start;
|
||||
while(pos < in.length() && in.charAt(pos) == ' ' && in.charAt(pos) != '\n')
|
||||
pos++;
|
||||
return pos < in.length() && in.charAt(pos) != '\n' ? pos : -1;
|
||||
}
|
||||
|
||||
private int readUntil(final StringBuilder out, final String in, int start, char... end)
|
||||
{
|
||||
int pos = start;
|
||||
while(pos < in.length() && in.charAt(pos) != '\n')
|
||||
{
|
||||
final char ch = in.charAt(pos);
|
||||
if(ch == '\\' && pos + 1 < in.length())
|
||||
{
|
||||
final char c;
|
||||
switch(c = in.charAt(pos + 1))
|
||||
{
|
||||
case '\\':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '#':
|
||||
case '"':
|
||||
case '\'':
|
||||
case '.':
|
||||
case '>':
|
||||
case '*':
|
||||
case '+':
|
||||
case '-':
|
||||
case '_':
|
||||
case '!':
|
||||
case '`':
|
||||
out.append(c);
|
||||
pos++;
|
||||
break;
|
||||
default:
|
||||
out.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean endReached = false;
|
||||
for(int n = 0; n < end.length; n++)
|
||||
{
|
||||
if(ch == end[n])
|
||||
{
|
||||
endReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(endReached)
|
||||
break;
|
||||
out.append(ch);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
final char ch = pos < in.length() ? in.charAt(pos) : '\n';
|
||||
for(int n = 0; n < end.length; n++)
|
||||
{
|
||||
if(ch == end[n])
|
||||
return pos;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int checkLink(final StringBuilder out, final String in, int start, MarkToken token)
|
||||
{
|
||||
int pos = start + (token == MarkToken.LINK ? 1 : 2);
|
||||
final StringBuilder temp = new StringBuilder();
|
||||
|
||||
temp.setLength(0);
|
||||
pos = this.readUntil(temp, in, pos, ']');
|
||||
if(pos < start)
|
||||
return -1;
|
||||
|
||||
String name = temp.toString(), link = null, comment = null;
|
||||
|
||||
pos++;
|
||||
pos = this.skipSpaces(in, pos);
|
||||
if(pos < start)
|
||||
return -1;
|
||||
if(in.charAt(pos) == '(')
|
||||
{
|
||||
pos++;
|
||||
temp.setLength(0);
|
||||
pos = this.readUntil(temp, in, pos, ' ', ')');
|
||||
if(pos < start)
|
||||
return -1;
|
||||
link = temp.toString();
|
||||
|
||||
if(in.charAt(pos) == ' ')
|
||||
{
|
||||
pos = this.skipSpaces(in, pos);
|
||||
if(pos > start && in.charAt(pos) == '"')
|
||||
{
|
||||
pos++;
|
||||
temp.setLength(0);
|
||||
pos = this.readUntil(temp, in, pos, '"');
|
||||
if(pos < start)
|
||||
return -1;
|
||||
comment = temp.toString();
|
||||
pos++;
|
||||
this.skipSpaces(link, pos);
|
||||
}
|
||||
}
|
||||
if(in.charAt(pos) != ')')
|
||||
return -1;
|
||||
}
|
||||
else if(in.charAt(pos) == '[')
|
||||
{
|
||||
pos++;
|
||||
temp.setLength(0);
|
||||
pos = this.readUntil(temp, in, pos, ']');
|
||||
if(pos < start)
|
||||
return -1;
|
||||
final String id = temp.length() > 0 ? temp.toString() : name;
|
||||
final LinkRef lr = this.linkRefs.get(id.toLowerCase());
|
||||
if(lr != null)
|
||||
{
|
||||
link = lr.link;
|
||||
comment = lr.title;
|
||||
}
|
||||
}
|
||||
|
||||
if(link == null)
|
||||
return -1;
|
||||
|
||||
if(token == MarkToken.LINK)
|
||||
{
|
||||
out.append("<a href=\"");
|
||||
this.appendCode(out, link, 0, link.length());
|
||||
out.append('"');
|
||||
if(comment != null)
|
||||
{
|
||||
out.append(" title=\"");
|
||||
this.appendCode(out, comment, 0, comment.length());
|
||||
out.append('"');
|
||||
}
|
||||
out.append('>');
|
||||
out.append(name);
|
||||
out.append("</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append("<img src=\"");
|
||||
this.appendCode(out, link, 0, link.length());
|
||||
out.append("\" alt=\"");
|
||||
this.appendCode(out, name, 0, name.length());
|
||||
out.append('"');
|
||||
if(comment != null)
|
||||
{
|
||||
out.append(" title=\"");
|
||||
this.appendCode(out, comment, 0, comment.length());
|
||||
out.append('"');
|
||||
}
|
||||
out.append(" />");
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
private int recursiveEmitLine(final StringBuilder out, final String in, int start, MarkToken token)
|
||||
{
|
||||
int pos = start, a, b;
|
||||
final StringBuilder temp = new StringBuilder();
|
||||
while(pos < in.length())
|
||||
{
|
||||
final MarkToken mt = this.getToken(in, pos);
|
||||
if(token != MarkToken.NONE && (mt == token || token == MarkToken.EM_STAR && mt == MarkToken.STRONG_STAR || token == MarkToken.EM_UNDERSCORE && mt == MarkToken.STRONG_UNDERSCORE))
|
||||
return pos;
|
||||
|
||||
switch(mt)
|
||||
{
|
||||
case IMAGE:
|
||||
case LINK:
|
||||
temp.setLength(0);
|
||||
b = this.checkLink(temp, in, pos, mt);
|
||||
if(b > 0)
|
||||
{
|
||||
out.append(temp);
|
||||
pos = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append(in.charAt(pos));
|
||||
}
|
||||
break;
|
||||
case EM_STAR:
|
||||
case EM_UNDERSCORE:
|
||||
temp.setLength(0);
|
||||
b = this.recursiveEmitLine(temp, in, pos + 1, mt);
|
||||
if(b > 0)
|
||||
{
|
||||
out.append("<em>");
|
||||
out.append(temp);
|
||||
out.append("</em>");
|
||||
pos = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append(in.charAt(pos));
|
||||
}
|
||||
break;
|
||||
case STRONG_STAR:
|
||||
case STRONG_UNDERSCORE:
|
||||
temp.setLength(0);
|
||||
b = this.recursiveEmitLine(temp, in, pos + 2, mt);
|
||||
if(b > 0)
|
||||
{
|
||||
out.append("<strong>");
|
||||
out.append(temp);
|
||||
out.append("</strong>");
|
||||
pos = b + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append(in.charAt(pos));
|
||||
}
|
||||
break;
|
||||
case CODE_SINGLE:
|
||||
case CODE_DOUBLE:
|
||||
a = pos + (mt == MarkToken.CODE_DOUBLE ? 2 : 1);
|
||||
b = this.findToken(in, a, mt);
|
||||
if(b > 0)
|
||||
{
|
||||
out.append("<code>");
|
||||
this.appendCode(out, in, a, b);
|
||||
out.append("</code>");
|
||||
pos = b + (mt == MarkToken.CODE_DOUBLE ? 1 : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.append(in.charAt(pos));
|
||||
}
|
||||
break;
|
||||
case ESCAPE:
|
||||
pos++;
|
||||
//$FALL-THROUGH$
|
||||
default:
|
||||
out.append(in.charAt(pos));
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private MarkToken getToken(final String in, final int pos)
|
||||
{
|
||||
final char c0 = pos > 0 ? in.charAt(pos - 1) : ' ';
|
||||
final char c = in.charAt(pos);
|
||||
final char c1 = pos + 1 < in.length() ? in.charAt(pos + 1) : ' ';
|
||||
final char c2 = pos + 2 < in.length() ? in.charAt(pos + 2) : ' ';
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case '*':
|
||||
if(c1 == '*')
|
||||
{
|
||||
return c0 != ' ' || c2 != ' ' ? MarkToken.STRONG_STAR : MarkToken.EM_STAR;
|
||||
}
|
||||
return c0 != ' ' || c1 != ' ' ? MarkToken.EM_STAR : MarkToken.NONE;
|
||||
case '_':
|
||||
if(c1 == '_')
|
||||
{
|
||||
return c0 != ' ' || c2 != ' ' ? MarkToken.STRONG_UNDERSCORE : MarkToken.EM_UNDERSCORE;
|
||||
}
|
||||
return c0 != ' ' || c1 != ' ' ? MarkToken.EM_UNDERSCORE : MarkToken.NONE;
|
||||
case '!':
|
||||
if(c1 == '[')
|
||||
return MarkToken.IMAGE;
|
||||
return MarkToken.NONE;
|
||||
case '[':
|
||||
return MarkToken.LINK;
|
||||
case '`':
|
||||
return c1 == '`' ? MarkToken.CODE_DOUBLE : MarkToken.CODE_SINGLE;
|
||||
case '\\':
|
||||
switch(c1)
|
||||
{
|
||||
case '\\':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '#':
|
||||
case '"':
|
||||
case '\'':
|
||||
case '.':
|
||||
case '>':
|
||||
case '*':
|
||||
case '+':
|
||||
case '-':
|
||||
case '_':
|
||||
case '!':
|
||||
case '`':
|
||||
return MarkToken.ESCAPE;
|
||||
default:
|
||||
return MarkToken.NONE;
|
||||
}
|
||||
default:
|
||||
return MarkToken.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private void emitMarkedLines(final StringBuilder out, final Line lines)
|
||||
{
|
||||
final StringBuilder in = new StringBuilder();
|
||||
Line line = lines;
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty)
|
||||
{
|
||||
in.append(line.value.substring(line.leading, line.value.length() - line.trailing));
|
||||
if(line.trailing >= 2)
|
||||
in.append("<br />");
|
||||
}
|
||||
if(line.next != null)
|
||||
in.append('\n');
|
||||
line = line.next;
|
||||
}
|
||||
|
||||
this.recursiveEmitLine(out, in.toString(), 0, MarkToken.NONE);
|
||||
}
|
||||
|
||||
private void emitRawLines(final StringBuilder out, final Line lines)
|
||||
{
|
||||
Line line = lines;
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty)
|
||||
{
|
||||
out.append(line.value);
|
||||
}
|
||||
if(line.next != null)
|
||||
out.append('\n');
|
||||
line = line.next;
|
||||
}
|
||||
}
|
||||
|
||||
private void emitCodeLines(final StringBuilder out, final Line lines)
|
||||
{
|
||||
Line line = lines;
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty)
|
||||
{
|
||||
for(int i = 4; i < line.value.length() - line.trailing; i++)
|
||||
{
|
||||
final char c;
|
||||
switch(c = line.value.charAt(i))
|
||||
{
|
||||
case '&':
|
||||
out.append("&");
|
||||
break;
|
||||
case '<':
|
||||
out.append("<");
|
||||
break;
|
||||
case '>':
|
||||
out.append(">");
|
||||
break;
|
||||
default:
|
||||
out.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(line.next != null)
|
||||
out.append('\n');
|
||||
line = line.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/java/txtmark/HTML.java
Normal file
54
src/java/txtmark/HTML.java
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
class HTML
|
||||
{
|
||||
private final static String[] names = new String[] {"Â", "â", "´", "Æ", "æ", "À", "à", "ℵ", "Α", "α", "&", "∧", "∠", "'", "Å", "å", "≈", "Ã", "ã", "Ä", "ä", "„", "Β", "β", "¦", "•", "∩", "Ç", "ç", "¸", "¢", "Χ", "χ", "ˆ", "♣", "≅", "©", "↵", "∪", "¤", "‡", "†", "⇓", "↓", "°", "Δ", "δ", "♦", "÷", "É", "é", "Ê", "ê", "È", "è", "∅", " ", " ", "Ε", "ε", "≡", "Η", "η", "Ð", "ð", "Ë", "ë", "€", "∃", "ƒ", "∀", "½", "¼", "¾", "⁄", "Γ", "γ", "≥", ">", "⇔", "↔", "♥", "…", "Í", "í", "Î", "î", "¡", "Ì", "ì", "ℑ", "∞", "∫", "Ι", "ι", "¿", "∈", "Ï", "ï", "Κ", "κ", "Λ", "λ", "⟨", "«", "⇐", "←", "⌈", "“", "≤", "⌊", "∗", "◊", "‎", "‹", "‘", "<", "¯", "—", "µ", "·", "−", "Μ", "μ", "∇", " ", "–", "≠", "∋", "¬", "∉", "⊄", "Ñ", "ñ", "Ν", "ν", "Ó", "ó", "Ô", "ô", "Œ", "œ", "Ò", "ò", "‾", "Ω", "ω", "Ο", "ο", "⊕", "∨", "ª", "º", "Ø", "ø", "Õ", "õ", "⊗", "Ö", "ö", "¶", "∂", "‰", "⊥", "Φ", "φ", "Π", "π", "ϖ", "±", "£", "″", "′", "∏", "∝", "Ψ", "ψ", """, "√", "⟩", "»", "⇒", "→", "⌉", "”", "ℜ", "®", "⌋", "Ρ", "ρ", "‏", "›", "’", "‚", "Š", "š", "⋅", "§", "­", "Σ", "σ", "ς", "∼", "♠", "⊂", "⊆", "∑", "⊃", "¹", "²", "³", "⊇", "ß", "Τ", "τ", "∴", "Θ", "θ", "ϑ", " ", "þ", "˜", "×", "™", "Ú", "ú", "⇑", "↑", "Û", "û", "Ù", "ù", "¨", "ϒ", "Υ", "υ", "Ü", "ü", "℘", "Ξ", "ξ", "Ý", "ý", "¥", "Ÿ", "ÿ", "Ζ", "ζ", "‍", "‌"};
|
||||
private final static char[] chars = new char[] {'\u00C2', '\u00E2', '\u00B4', '\u00C6', '\u00E6', '\u00C0', '\u00E0', '\u2135', '\u0391', '\u03B1', '\u0026', '\u2227', '\u2220', '\'', '\u00C5', '\u00E5', '\u2248', '\u00C3', '\u00E3', '\u00C4', '\u00E4', '\u201E', '\u0392', '\u03B2', '\u00A6', '\u2022', '\u2229', '\u00C7', '\u00E7', '\u00B8', '\u00A2', '\u03A7', '\u03C7', '\u02C6', '\u2663', '\u2245', '\u00A9', '\u21B5', '\u222A', '\u00A4', '\u2021', '\u2020', '\u21D3', '\u2193', '\u00B0', '\u0394', '\u03B4', '\u2666', '\u00F7', '\u00C9', '\u00E9', '\u00CA', '\u00EA', '\u00C8', '\u00E8', '\u2205', '\u2003', '\u2002', '\u0395', '\u03B5', '\u2261', '\u0397', '\u03B7', '\u00D0', '\u00F0', '\u00CB', '\u00EB', '\u20AC', '\u2203', '\u0192', '\u2200', '\u00BD', '\u00BC', '\u00BE', '\u2044', '\u0393', '\u03B3', '\u2265', '\u003E', '\u21D4', '\u2194', '\u2665', '\u2026', '\u00CD', '\u00ED', '\u00CE', '\u00EE', '\u00A1', '\u00CC', '\u00EC', '\u2111', '\u221E', '\u222B', '\u0399', '\u03B9', '\u00BF', '\u2208', '\u00CF', '\u00EF', '\u039A', '\u03BA', '\u039B', '\u03BB', '\u2329', '\u00AB', '\u21D0', '\u2190', '\u2308', '\u201C', '\u2264', '\u230A', '\u2217', '\u25CA', '\u200E', '\u2039', '\u2018', '\u003C', '\u00AF', '\u2014', '\u00B5', '\u00B7', '\u2212', '\u039C', '\u03BC', '\u2207', '\u00A0', '\u2013', '\u2260', '\u220B', '\u00AC', '\u2209', '\u2284', '\u00D1', '\u00F1', '\u039D', '\u03BD', '\u00D3', '\u00F3', '\u00D4', '\u00F4', '\u0152', '\u0153', '\u00D2', '\u00F2', '\u203E', '\u03A9', '\u03C9', '\u039F', '\u03BF', '\u2295', '\u2228', '\u00AA', '\u00BA', '\u00D8', '\u00F8', '\u00D5', '\u00F5', '\u2297', '\u00D6', '\u00F6', '\u00B6', '\u2202', '\u2030', '\u22A5', '\u03A6', '\u03C6', '\u03A0', '\u03C0', '\u03D6', '\u00B1', '\u00A3', '\u2033', '\u2032', '\u220F', '\u221D', '\u03A8', '\u03C8', '\u0022', '\u221A', '\u232A', '\u00BB', '\u21D2', '\u2192', '\u2309', '\u201D', '\u211C', '\u00AE', '\u230B', '\u03A1', '\u03C1', '\u200F', '\u203A', '\u2019', '\u201A', '\u0160', '\u0161', '\u22C5', '\u00A7', '\u00AD', '\u03A3', '\u03C3', '\u03C2', '\u223C', '\u2660', '\u2282', '\u2286', '\u2211', '\u2283', '\u00B9', '\u00B2', '\u00B3', '\u2287', '\u00DF', '\u03A4', '\u03C4', '\u2234', '\u0398', '\u03B8', '\u03D1', '\u00DE', '\u00FE', '\u02DC', '\u00D7', '\u2122', '\u00DA', '\u00FA', '\u21D1', '\u2191', '\u00DB', '\u00FB', '\u00D9', '\u00F9', '\u00A8', '\u03D2', '\u03A5', '\u03C5', '\u00DC', '\u00FC', '\u2118', '\u039E', '\u03BE', '\u00DD', '\u00FD', '\u00A5', '\u0178', '\u00FF', '\u0396', '\u03B6', '\u200D', '\u200C'};
|
||||
private final static HashMap<Character, String> encodeMap = new HashMap<Character, String>();
|
||||
private final static HashMap<String, Character> decodeMap = new HashMap<String, Character>();
|
||||
private final static HashSet<String> HTML_ELEMENTS = new HashSet<String>();
|
||||
private final static HashSet<String> HTML_BLOCK_ELEMENTS = new HashSet<String>();
|
||||
|
||||
private final static HTMLElement[] BLOCK_ELEMENTS = new HTMLElement[] {
|
||||
HTMLElement.address,
|
||||
HTMLElement.blockquote,
|
||||
HTMLElement.del, HTMLElement.div, HTMLElement.dl,
|
||||
HTMLElement.fieldset, HTMLElement.form,
|
||||
HTMLElement.h1, HTMLElement.h2, HTMLElement.h3, HTMLElement.h4, HTMLElement.h5, HTMLElement.h6, HTMLElement.hr,
|
||||
HTMLElement.ins,
|
||||
HTMLElement.noscript,
|
||||
HTMLElement.ol,
|
||||
HTMLElement.p, HTMLElement.pre,
|
||||
HTMLElement.table,
|
||||
HTMLElement.ul
|
||||
};
|
||||
|
||||
static
|
||||
{
|
||||
for(final HTMLElement h : HTMLElement.values())
|
||||
{
|
||||
HTML_ELEMENTS.add(h.toString());
|
||||
}
|
||||
for(final HTMLElement h : BLOCK_ELEMENTS)
|
||||
{
|
||||
HTML_BLOCK_ELEMENTS.add(h.toString());
|
||||
}
|
||||
for(int i = 0; i < names.length; i++)
|
||||
{
|
||||
encodeMap.put(chars[i], names[i]);
|
||||
decodeMap.put(names[i], chars[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private HTML()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
28
src/java/txtmark/HTMLElement.java
Normal file
28
src/java/txtmark/HTMLElement.java
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
enum HTMLElement
|
||||
{
|
||||
a, abbr, acronym, address, applet, area,
|
||||
b, base, basefont, bdo, big, blockquote, body, br, button,
|
||||
caption, cite, code, col, colgroup,
|
||||
dd, del, dfn, div, dl, dt,
|
||||
em,
|
||||
fieldset, font, form, frame, frameset,
|
||||
h1, h2, h3, h4, h5, h6, head, hr, html,
|
||||
i, iframe, img, input, ins,
|
||||
kbd,
|
||||
label, legend, li, link,
|
||||
map, meta,
|
||||
noscript,
|
||||
object, ol, optgroup, option,
|
||||
p, param, pre,
|
||||
q,
|
||||
s, samp, script, select, small, span, strike, strong, style, sub, sup,
|
||||
table, tbody, td, textarea, tfoot, th, thead, title, tr, tt,
|
||||
u, ul,
|
||||
var
|
||||
}
|
||||
207
src/java/txtmark/Line.java
Normal file
207
src/java/txtmark/Line.java
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
class Line
|
||||
{
|
||||
public int pos;
|
||||
public int leading = 0, trailing = 0;
|
||||
public boolean isEmpty = true;
|
||||
public String value = null;
|
||||
public Line previous = null, next = null;
|
||||
public boolean prevEmpty, nextEmpty;
|
||||
|
||||
public Line()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
this.leading = 0;
|
||||
while(this.leading < this.value.length() && this.value.charAt(this.leading) == ' ')
|
||||
this.leading++;
|
||||
|
||||
if(this.leading == this.value.length())
|
||||
{
|
||||
this.setEmpty();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.isEmpty = false;
|
||||
this.trailing = 0;
|
||||
while(this.value.charAt(this.value.length() - this.trailing - 1) == ' ')
|
||||
this.trailing++;
|
||||
}
|
||||
}
|
||||
|
||||
public void initLeading()
|
||||
{
|
||||
this.leading = 0;
|
||||
while(this.leading < this.value.length() && this.value.charAt(this.leading) == ' ')
|
||||
this.leading++;
|
||||
|
||||
if(this.leading == this.value.length())
|
||||
{
|
||||
this.setEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean skipSpaces()
|
||||
{
|
||||
while(this.pos < this.value.length() && this.value.charAt(this.pos) == ' ')
|
||||
this.pos++;
|
||||
return this.pos < this.value.length();
|
||||
}
|
||||
|
||||
public String readUntil(char... end)
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
int pos = this.pos;
|
||||
while(pos < this.value.length())
|
||||
{
|
||||
final char ch = this.value.charAt(pos);
|
||||
if(ch == '\\' && pos + 1 < this.value.length())
|
||||
{
|
||||
final char c;
|
||||
switch(c = this.value.charAt(pos + 1))
|
||||
{
|
||||
case '\\':
|
||||
case '[':
|
||||
case ']':
|
||||
case '(':
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
case '#':
|
||||
case '"':
|
||||
case '\'':
|
||||
case '.':
|
||||
case '>':
|
||||
case '*':
|
||||
case '+':
|
||||
case '-':
|
||||
case '_':
|
||||
case '!':
|
||||
case '`':
|
||||
sb.append(c);
|
||||
pos++;
|
||||
break;
|
||||
default:
|
||||
sb.append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean endReached = false;
|
||||
for(int n = 0; n < end.length; n++)
|
||||
{
|
||||
if(ch == end[n])
|
||||
{
|
||||
endReached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(endReached)
|
||||
break;
|
||||
sb.append(ch);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
final char ch = pos < this.value.length() ? this.value.charAt(pos) : '\n';
|
||||
for(int n = 0; n < end.length; n++)
|
||||
{
|
||||
if(ch == end[n])
|
||||
{
|
||||
this.pos = pos;
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setEmpty()
|
||||
{
|
||||
this.value = "";
|
||||
this.leading = this.trailing = 0;
|
||||
this.isEmpty = true;
|
||||
if(this.previous != null)
|
||||
this.previous.nextEmpty = true;
|
||||
if(this.next != null)
|
||||
this.next.prevEmpty = true;
|
||||
}
|
||||
|
||||
private int countCharsWs(char ch)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = 0; i < this.value.length(); i++)
|
||||
{
|
||||
final char c = this.value.charAt(i);
|
||||
if(c == ' ')
|
||||
continue;
|
||||
if(c == ch)
|
||||
{
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public LineType getLineType()
|
||||
{
|
||||
if(this.isEmpty)
|
||||
return LineType.EMPTY;
|
||||
|
||||
if(this.leading > 3)
|
||||
return LineType.CODE;
|
||||
|
||||
if(this.value.charAt(this.leading) == '#')
|
||||
return LineType.HEADLINE;
|
||||
|
||||
if(this.value.charAt(this.leading) == '>')
|
||||
return LineType.BQUOTE;
|
||||
|
||||
if(this.leading == 0 && this.value.length() > 2 && (this.value.charAt(0) == '*' || this.value.charAt(0) == '-'))
|
||||
{
|
||||
if(this.countCharsWs(this.value.charAt(0)) >= 3)
|
||||
return LineType.HR;
|
||||
}
|
||||
|
||||
if(this.value.length() - this.leading >= 2 && this.value.charAt(this.leading + 1) == ' ')
|
||||
{
|
||||
switch(this.value.charAt(this.leading))
|
||||
{
|
||||
case '*':
|
||||
case '-':
|
||||
case '+':
|
||||
return LineType.ULIST;
|
||||
}
|
||||
}
|
||||
|
||||
if(this.value.length() - this.leading >= 3 && Character.isDigit(this.value.charAt(this.leading)))
|
||||
{
|
||||
int i = this.leading + 1;
|
||||
while(i < this.value.length() && Character.isDigit(this.value.charAt(i)))
|
||||
i++;
|
||||
if(i + 1 < this.value.length() && this.value.charAt(i) == '.' && this.value.charAt(i + 1) == ' ')
|
||||
return LineType.OLIST;
|
||||
}
|
||||
|
||||
if(this.next != null && !this.next.isEmpty)
|
||||
{
|
||||
if((this.next.value.charAt(0) == '-') && (this.next.countCharsWs('-') > 0))
|
||||
return LineType.HEADLINE2;
|
||||
if((this.next.value.charAt(0) == '=') && (this.next.countCharsWs('=') > 0))
|
||||
return LineType.HEADLINE1;
|
||||
}
|
||||
|
||||
return LineType.OTHER;
|
||||
}
|
||||
}
|
||||
16
src/java/txtmark/LineType.java
Normal file
16
src/java/txtmark/LineType.java
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
enum LineType
|
||||
{
|
||||
EMPTY,
|
||||
OTHER,
|
||||
HEADLINE, HEADLINE1, HEADLINE2,
|
||||
CODE,
|
||||
ULIST, OLIST,
|
||||
BQUOTE,
|
||||
HR
|
||||
}
|
||||
23
src/java/txtmark/LinkRef.java
Normal file
23
src/java/txtmark/LinkRef.java
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
class LinkRef
|
||||
{
|
||||
public final String link;
|
||||
public String title;
|
||||
|
||||
public LinkRef(final String link, final String title)
|
||||
{
|
||||
this.link = link;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.link + " \"" + this.title + "\"";
|
||||
}
|
||||
}
|
||||
21
src/java/txtmark/MarkToken.java
Normal file
21
src/java/txtmark/MarkToken.java
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
enum MarkToken
|
||||
{
|
||||
NONE,
|
||||
EM_STAR, // x*x
|
||||
EM_UNDERSCORE, // x_x
|
||||
STRONG_STAR, // x**x
|
||||
STRONG_UNDERSCORE, // x__x
|
||||
CODE_SINGLE, // `
|
||||
CODE_DOUBLE, // ``
|
||||
LINK, // [
|
||||
HTML, // <
|
||||
IMAGE, // ![
|
||||
ENTITY, // &
|
||||
ESCAPE // \x
|
||||
}
|
||||
384
src/java/txtmark/Processor.java
Normal file
384
src/java/txtmark/Processor.java
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
|
||||
* See LICENSE.txt for licensing information.
|
||||
*/
|
||||
package txtmark;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
public class Processor
|
||||
{
|
||||
private final Reader reader;
|
||||
private Emitter emitter = new Emitter();
|
||||
|
||||
private Processor(Reader reader)
|
||||
{
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public static String process(final String input) throws IOException
|
||||
{
|
||||
return process(new StringReader(input));
|
||||
}
|
||||
|
||||
public static String process(final File file) throws IOException
|
||||
{
|
||||
return process(file, "UTF-8");
|
||||
}
|
||||
|
||||
public static String process(final File file, final String encoding) throws IOException
|
||||
{
|
||||
final Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), encoding));
|
||||
final Processor p = new Processor(r);
|
||||
final String ret = p.process();
|
||||
r.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static String process(final InputStream input) throws IOException
|
||||
{
|
||||
return process(input, "UTF-8");
|
||||
}
|
||||
|
||||
public static String process(final InputStream input, final String encoding) throws IOException
|
||||
{
|
||||
final Processor p = new Processor(new BufferedReader(new InputStreamReader(input, encoding)));
|
||||
return p.process();
|
||||
}
|
||||
|
||||
public static String process(final Reader reader) throws IOException
|
||||
{
|
||||
final Processor p = new Processor(!(reader instanceof BufferedReader) ? new BufferedReader(reader) : reader);
|
||||
return p.process();
|
||||
}
|
||||
|
||||
private Block readLines() throws IOException
|
||||
{
|
||||
final Block block = new Block();
|
||||
final StringBuilder sb = new StringBuilder(80);
|
||||
int c = this.reader.read();
|
||||
LinkRef lastLinkRef = null;
|
||||
while(c != -1)
|
||||
{
|
||||
sb.setLength(0);
|
||||
int pos = 0;
|
||||
boolean eol = false;
|
||||
while(!eol)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case -1:
|
||||
eol = true;
|
||||
break;
|
||||
case '\n':
|
||||
c = this.reader.read();
|
||||
if(c == '\r')
|
||||
c = this.reader.read();
|
||||
eol = true;
|
||||
break;
|
||||
case '\r':
|
||||
c = this.reader.read();
|
||||
if(c == '\n')
|
||||
c = this.reader.read();
|
||||
eol = true;
|
||||
break;
|
||||
case '\t':
|
||||
{
|
||||
final int np = pos + (4 - (pos & 3));
|
||||
while(pos < np)
|
||||
{
|
||||
sb.append(' ');
|
||||
pos++;
|
||||
}
|
||||
c = this.reader.read();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pos++;
|
||||
sb.append((char)c);
|
||||
c = this.reader.read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final Line line = new Line();
|
||||
line.value = sb.toString();
|
||||
line.init();
|
||||
|
||||
// Check for link definitions
|
||||
boolean isLinkRef = false;
|
||||
String id = null, link = null, comment = null;
|
||||
if(!line.isEmpty && line.leading < 4 && line.value.charAt(line.leading) == '[')
|
||||
{
|
||||
line.pos = line.leading + 1;
|
||||
// Read ID up to ']'
|
||||
id = line.readUntil(']');
|
||||
// Is ID valid and are there any more characters?
|
||||
if(id != null && line.pos + 2 < line.value.length())
|
||||
{
|
||||
// Check for ':' ([...]:...)
|
||||
if(line.value.charAt(line.pos + 1) == ':')
|
||||
{
|
||||
line.pos += 2;
|
||||
line.skipSpaces();
|
||||
// Check for link syntax
|
||||
if(line.value.charAt(line.pos) == '<')
|
||||
{
|
||||
line.pos++;
|
||||
link = line.readUntil('>');
|
||||
line.pos++;
|
||||
}
|
||||
else
|
||||
link = line.readUntil(' ', '\n');
|
||||
|
||||
// Is link valid?
|
||||
if(link != null)
|
||||
{
|
||||
// Any non-whitespace characters following?
|
||||
if(line.skipSpaces())
|
||||
{
|
||||
final char ch = line.value.charAt(line.pos);
|
||||
// Read comment
|
||||
if(ch == '\"' || ch == '\'' || ch == '(')
|
||||
{
|
||||
line.pos++;
|
||||
comment = line.readUntil(ch == '(' ? ')' : ch);
|
||||
// Valid linkRef only if comment is valid
|
||||
if(comment != null)
|
||||
isLinkRef = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
isLinkRef = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isLinkRef)
|
||||
{
|
||||
// Store linkRef and skip line
|
||||
final LinkRef lr = new LinkRef(link, comment);
|
||||
this.emitter.addLinkRef(id, lr);
|
||||
if(comment == null)
|
||||
lastLinkRef = lr;
|
||||
}
|
||||
else
|
||||
{
|
||||
comment = null;
|
||||
// Check for multi-line linkRef
|
||||
if(!line.isEmpty && lastLinkRef != null)
|
||||
{
|
||||
line.pos = line.leading;
|
||||
final char ch = line.value.charAt(line.pos);
|
||||
if(ch == '\"' || ch == '\'' || ch == '(')
|
||||
{
|
||||
line.pos++;
|
||||
comment = line.readUntil(ch == '(' ? ')' : ch);
|
||||
}
|
||||
if(comment != null)
|
||||
lastLinkRef.title = comment;
|
||||
|
||||
lastLinkRef = null;
|
||||
}
|
||||
|
||||
// No multi-line linkRef, store line
|
||||
if(comment == null)
|
||||
{
|
||||
line.pos = 0;
|
||||
block.appendLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
private void initListBlock(final Block root)
|
||||
{
|
||||
Line line = root.lines;
|
||||
line = line.next;
|
||||
while(line != null)
|
||||
{
|
||||
final LineType t = line.getLineType();
|
||||
if(
|
||||
(t == LineType.OLIST || t == LineType.ULIST) ||
|
||||
(!line.isEmpty && (line.prevEmpty && line.leading == 0 && !(t == LineType.OLIST || t == LineType.ULIST))))
|
||||
{
|
||||
root.split(line.previous).type = BlockType.LIST_ITEM;
|
||||
}
|
||||
line = line.next;
|
||||
}
|
||||
root.split(root.lineTail).type = BlockType.LIST_ITEM;
|
||||
}
|
||||
|
||||
private void recurse(final Block root, boolean listMode)
|
||||
{
|
||||
Block block;
|
||||
Line line = root.lines;
|
||||
while(line != null && line.isEmpty) line = line.next;
|
||||
if(line == null)
|
||||
return;
|
||||
|
||||
if(listMode)
|
||||
root.removeListIndent();
|
||||
|
||||
boolean hasParagraph = false;
|
||||
|
||||
while(line != null)
|
||||
{
|
||||
final LineType type = line.getLineType();
|
||||
switch(type)
|
||||
{
|
||||
case OTHER:
|
||||
{
|
||||
final boolean wasEmpty = line.prevEmpty;
|
||||
while(line != null && !line.isEmpty)
|
||||
{
|
||||
final LineType t = line.getLineType();
|
||||
if(listMode && (t == LineType.OLIST || t == LineType.ULIST))
|
||||
break;
|
||||
if(t == LineType.HEADLINE || t == LineType.HEADLINE1 || t == LineType.HEADLINE2 || t == LineType.HR || t == LineType.BQUOTE)
|
||||
break;
|
||||
line = line.next;
|
||||
}
|
||||
final BlockType bt;
|
||||
if(line != null && !line.isEmpty)
|
||||
{
|
||||
bt = (listMode && root.blocks == null && !wasEmpty) ? BlockType.NONE : BlockType.PARAGRAPH;
|
||||
root.split(line.previous).type = bt;
|
||||
root.removeLeadingEmptyLines();
|
||||
}
|
||||
else
|
||||
{
|
||||
bt = (listMode && (line == null || !line.isEmpty) && !wasEmpty) ? BlockType.NONE : BlockType.PARAGRAPH;
|
||||
root.split(line == null ? root.lineTail : line).type = bt;
|
||||
root.removeLeadingEmptyLines();
|
||||
}
|
||||
hasParagraph = bt == BlockType.PARAGRAPH;
|
||||
line = root.lines;
|
||||
}
|
||||
break;
|
||||
case CODE:
|
||||
while(line != null && (line.isEmpty || line.leading > 3))
|
||||
{
|
||||
line = line.next;
|
||||
}
|
||||
block = root.split(line != null ? line.previous : root.lineTail);
|
||||
block.type = BlockType.CODE;
|
||||
block.removeSurroundingEmptyLines();
|
||||
break;
|
||||
case BQUOTE:
|
||||
while(line != null)
|
||||
{
|
||||
if(!line.isEmpty && (line.prevEmpty && line.leading == 0 && line.getLineType() != LineType.BQUOTE))
|
||||
break;
|
||||
line = line.next;
|
||||
}
|
||||
block = root.split(line != null ? line.previous : root.lineTail);
|
||||
block.type = BlockType.BLOCKQUOTE;
|
||||
block.removeSurroundingEmptyLines();
|
||||
block.removeBlockQuotePrefix();
|
||||
this.recurse(block, false);
|
||||
line = root.lines;
|
||||
break;
|
||||
case HR:
|
||||
if(line.previous != null)
|
||||
{
|
||||
root.split(line.previous);
|
||||
}
|
||||
root.split(line).type = BlockType.RULER;
|
||||
root.removeLeadingEmptyLines();
|
||||
line = root.lines;
|
||||
break;
|
||||
case HEADLINE:
|
||||
case HEADLINE1:
|
||||
case HEADLINE2:
|
||||
if(line.previous != null)
|
||||
{
|
||||
root.split(line.previous);
|
||||
}
|
||||
if(type != LineType.HEADLINE)
|
||||
{
|
||||
line.next.setEmpty();
|
||||
}
|
||||
block = root.split(line);
|
||||
block.type = BlockType.HEADLINE;
|
||||
if(type != LineType.HEADLINE)
|
||||
block.hlDepth = type == LineType.HEADLINE1 ? 1 : 2;
|
||||
block.transfromHeadline();
|
||||
root.removeLeadingEmptyLines();
|
||||
line = root.lines;
|
||||
break;
|
||||
case OLIST:
|
||||
case ULIST:
|
||||
while(line != null)
|
||||
{
|
||||
final LineType t = line.getLineType();
|
||||
if(!line.isEmpty && (line.prevEmpty && line.leading == 0 && !(t == LineType.OLIST || t == LineType.ULIST)))
|
||||
break;
|
||||
line = line.next;
|
||||
}
|
||||
block = root.split(line != null ? line.previous : root.lineTail);
|
||||
block.type = type == LineType.OLIST ? BlockType.ORDERED_LIST : BlockType.UNORDERED_LIST;
|
||||
block.lines.prevEmpty = false;
|
||||
block.lineTail.nextEmpty = false;
|
||||
block.removeSurroundingEmptyLines();
|
||||
block.lines.prevEmpty = block.lineTail.nextEmpty = false;
|
||||
this.initListBlock(block);
|
||||
block = block.blocks;
|
||||
while(block != null)
|
||||
{
|
||||
this.recurse(block, true);
|
||||
block = block.next;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
line = line.next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(listMode && hasParagraph)
|
||||
{
|
||||
block = root.blocks;
|
||||
while(block != null)
|
||||
{
|
||||
if(block.type == BlockType.NONE)
|
||||
block.type = BlockType.PARAGRAPH;
|
||||
block = block.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String process() throws IOException
|
||||
{
|
||||
final StringBuilder out = new StringBuilder();
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
|
||||
final Block parent = this.readLines();
|
||||
parent.removeSurroundingEmptyLines();
|
||||
|
||||
this.recurse(parent, false);
|
||||
Block block = parent.blocks;
|
||||
while(block != null)
|
||||
{
|
||||
this.emitter.emit(out, block);
|
||||
block = block.next;
|
||||
}
|
||||
|
||||
t0 = System.nanoTime() - t0;
|
||||
|
||||
out.append(String.format("\n<!-- Processing time: %dms -->\n", (int)(t0 * 1e-6)));
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user