
com.github.rjeschke.txtmark.Utils Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2011 René Jeschke
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.rjeschke.txtmark;
/**
* Utilities.
*
* @author René Jeschke
*/
class Utils
{
/** Random number generator value. */
private static int RND = (int)System.nanoTime();
/**
* LCG random number generator.
*
* @return A pseudo random number between 0 and 1023
*/
public final static int rnd()
{
return (RND = RND * 1664525 + 1013904223) >>> 22;
}
/**
* Skips spaces in the given String.
*
* @param in
* Input String.
* @param start
* Starting position.
* @return The new position or -1 if EOL has been reached.
*/
public final static int skipSpaces(final String in, final int start)
{
int pos = start;
while(pos < in.length() && (in.charAt(pos) == ' ' || in.charAt(pos) == '\n'))
pos++;
return pos < in.length() ? pos : -1;
}
/**
* Processed the given escape sequence.
*
* @param out
* The StringBuilder to write to.
* @param ch
* The character.
* @param pos
* Current parsing position.
* @return The new position.
*/
public final static int escape(final StringBuilder out, final char ch, final int pos)
{
switch(ch)
{
case '\\':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case '#':
case '"':
case '\'':
case '.':
case '>':
case '<':
case '*':
case '+':
case '-':
case '_':
case '!':
case '`':
case '^':
out.append(ch);
return pos + 1;
default:
out.append('\\');
return pos;
}
}
/**
* Reads characters until any 'end' character is encountered.
*
* @param out
* The StringBuilder to write to.
* @param in
* The Input String.
* @param start
* Starting position.
* @param end
* End characters.
* @return The new position or -1 if no 'end' char was found.
*/
public final static int readUntil(final StringBuilder out, final String in, final int start, final char... end)
{
int pos = start;
while(pos < in.length())
{
final char ch = in.charAt(pos);
if(ch == '\\' && pos + 1 < in.length())
{
pos = escape(out, in.charAt(pos + 1), pos);
}
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++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Reads characters until the 'end' character is encountered.
*
* @param out
* The StringBuilder to write to.
* @param in
* The Input String.
* @param start
* Starting position.
* @param end
* End characters.
* @return The new position or -1 if no 'end' char was found.
*/
public final static int readUntil(final StringBuilder out, final String in, final int start, final char end)
{
int pos = start;
while(pos < in.length())
{
final char ch = in.charAt(pos);
if(ch == '\\' && pos + 1 < in.length())
{
pos = escape(out, in.charAt(pos + 1), pos);
}
else
{
if(ch == end)
break;
out.append(ch);
}
pos++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Reads a markdown link.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Starting position.
* @return The new position or -1 if this is no valid markdown link.
*/
public final static int readMdLink(final StringBuilder out, final String in, final int start)
{
int pos = start;
int counter = 1;
while(pos < in.length())
{
final char ch = in.charAt(pos);
if(ch == '\\' && pos + 1 < in.length())
{
pos = escape(out, in.charAt(pos + 1), pos);
}
else
{
boolean endReached = false;
switch(ch)
{
case '(':
counter++;
break;
case ' ':
if(counter == 1)
endReached = true;
break;
case ')':
counter--;
if(counter == 0)
endReached = true;
break;
}
if(endReached)
break;
out.append(ch);
}
pos++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Reads a markdown link ID.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Starting position.
* @return The new position or -1 if this is no valid markdown link ID.
*/
public final static int readMdLinkId(final StringBuilder out, final String in, final int start)
{
int pos = start;
int counter = 1;
while(pos < in.length())
{
final char ch = in.charAt(pos);
boolean endReached = false;
switch(ch)
{
case '\n':
out.append(' ');
break;
case '[':
counter++;
out.append(ch);
break;
case ']':
counter--;
if(counter == 0)
endReached = true;
else
out.append(ch);
break;
default:
out.append(ch);
break;
}
if(endReached)
break;
pos++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Reads characters until any 'end' character is encountered, ignoring
* escape sequences.
*
* @param out
* The StringBuilder to write to.
* @param in
* The Input String.
* @param start
* Starting position.
* @param end
* End characters.
* @return The new position or -1 if no 'end' char was found.
*/
public final static int readRawUntil(final StringBuilder out, final String in, final int start, final char... end)
{
int pos = start;
while(pos < in.length())
{
final char ch = in.charAt(pos);
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++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Reads characters until the end character is encountered, ignoring escape
* sequences.
*
* @param out
* The StringBuilder to write to.
* @param in
* The Input String.
* @param start
* Starting position.
* @param end
* End characters.
* @return The new position or -1 if no 'end' char was found.
*/
public final static int readRawUntil(final StringBuilder out, final String in, final int start, final char end)
{
int pos = start;
while(pos < in.length())
{
final char ch = in.charAt(pos);
if(ch == end)
break;
out.append(ch);
pos++;
}
return (pos == in.length()) ? -1 : pos;
}
/**
* Appends the given string encoding special HTML characters.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Input String starting position.
* @param end
* Input String end position.
*/
public final static void appendCode(final StringBuilder out, final String in, final int start, final 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;
}
}
}
/**
* Appends the given string encoding special HTML characters (used in HTML
* attribute values).
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Input String starting position.
* @param end
* Input String end position.
*/
public final static void appendValue(final StringBuilder out, final String in, final int start, final 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;
case '"':
out.append(""");
break;
case '\'':
out.append("'");
break;
default:
out.append(c);
break;
}
}
}
/**
* Append the given char as a decimal HTML entity.
*
* @param out
* The StringBuilder to write to.
* @param value
* The character.
*/
public final static void appendDecEntity(final StringBuilder out, final char value)
{
out.append("");
out.append((int)value);
out.append(';');
}
/**
* Append the given char as a hexadecimal HTML entity.
*
* @param out
* The StringBuilder to write to.
* @param value
* The character.
*/
public final static void appendHexEntity(final StringBuilder out, final char value)
{
out.append("");
out.append(Integer.toHexString(value));
out.append(';');
}
/**
* Appends the given mailto link using obfuscation.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Input String starting position.
* @param end
* Input String end position.
*/
public final static void appendMailto(final StringBuilder out, final String in, final int start, final int end)
{
for(int i = start; i < end; i++)
{
final char c;
final int r = rnd();
switch(c = in.charAt(i))
{
case '&':
case '<':
case '>':
case '"':
case '\'':
case '@':
if(r < 512)
appendDecEntity(out, c);
else
appendHexEntity(out, c);
break;
default:
if(r < 32)
out.append(c);
else if(r < 520)
appendDecEntity(out, c);
else
appendHexEntity(out, c);
break;
}
}
}
/**
* Extracts the tag from an XML element.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input StringBuilder.
*/
public final static void getXMLTag(final StringBuilder out, final StringBuilder in)
{
int pos = 1;
if(in.charAt(1) == '/')
pos++;
while(Character.isLetterOrDigit(in.charAt(pos)))
{
out.append(in.charAt(pos++));
}
}
/**
* Extracts the tag from an XML element.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
*/
public final static void getXMLTag(final StringBuilder out, final String in)
{
int pos = 1;
if(in.charAt(1) == '/')
pos++;
while(Character.isLetterOrDigit(in.charAt(pos)))
{
out.append(in.charAt(pos++));
}
}
/**
* Reads an XML element.
*
* @param out
* The StringBuilder to write to.
* @param in
* Input String.
* @param start
* Starting position.
* @param safeMode
* Whether to escape unsafe HTML tags or not
* @return The new position or -1 if this is no valid XML element.
*/
public final static int readXML(final StringBuilder out, final String in, final int start, final boolean safeMode)
{
int pos;
final boolean isCloseTag;
try
{
if(in.charAt(start + 1) == '/')
{
isCloseTag = true;
pos = start + 2;
}
else if(in.charAt(start + 1) == '!')
{
out.append("');
if(pos == -1)
return -1;
final String tag = temp.toString().trim().toLowerCase();
if(HTML.isUnsafeHtmlElement(tag))
{
out.append("<");
if(isCloseTag)
out.append('/');
out.append(temp);
}
}
else
{
out.append('<');
if(isCloseTag)
out.append('/');
pos = readRawUntil(out, in, pos, ' ', '/', '>');
}
if(pos == -1)
return -1;
pos = readRawUntil(out, in, pos, '/', '>');
if(in.charAt(pos) == '/')
{
out.append(" /");
pos = readRawUntil(out, in, pos + 1, '>');
if(pos == -1)
return -1;
}
if(in.charAt(pos) == '>')
{
out.append('>');
return pos;
}
}
catch (StringIndexOutOfBoundsException e)
{
return -1;
}
return -1;
}
/**
* Appends the given string to the given StringBuilder, replacing '&',
* '<' and '>' by their respective HTML entities.
*
* @param out
* The StringBuilder to append to.
* @param value
* The string to append.
* @param offset
* The character offset into value from where to start
*/
public final static void codeEncode(StringBuilder out, String value, int offset)
{
for(int i = offset; i < value.length(); i++)
{
final char c = value.charAt(i);
switch(c)
{
case '&':
out.append("&");
break;
case '<':
out.append("<");
break;
case '>':
out.append(">");
break;
default:
out.append(c);
}
}
}
/**
* Removes trailing `
and trims spaces.
*
* @param fenceLine
* Fenced code block starting line
* @return Rest of the line after trimming and backtick removal
* @since 0.7
*/
public final static String getMetaFromFence(String fenceLine)
{
for(int i = 0; i < fenceLine.length(); i++)
{
final char c = fenceLine.charAt(i);
if(!Character.isWhitespace(c) && c != '`' && c != '~' && c != '%')
{
return fenceLine.substring(i).trim();
}
}
return "";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy