Mercurial Hosting > luan
changeset 1702:8ad468cc88d4
add goodjava/bbcode
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 30 Jun 2022 20:04:34 -0600 |
parents | 077366d117bb |
children | 3a61451f8130 |
files | src/goodjava/bbcode/BBCode.java src/luan/modules/Parsers.luan src/luan/modules/parsers/BBCode.java src/luan/modules/parsers/BBCodeLuan.java |
diffstat | 4 files changed, 391 insertions(+), 335 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/goodjava/bbcode/BBCode.java Thu Jun 30 20:04:34 2022 -0600 @@ -0,0 +1,334 @@ +package goodjava.bbcode; + +import java.util.List; +import java.util.ArrayList; +import goodjava.parser.Parser; + + +public final class BBCode { + + public enum Target { HTML, TEXT } + + public interface Quoter { + public String quote(Target target,String text,String param); + } + + public static final Quoter defaultQuoter = new Quoter() { + public String quote(Target target,String text,String param) { + StringBuilder sb = new StringBuilder(); + sb.append( "<blockquote>" ); + if( param != null ) { + sb.append( htmlEncode(param) ); + sb.append( " wrote:\n" ); + } + sb.append( text ); + sb.append( "</blockquote>" ); + return sb.toString(); + } + }; + + public Target target = Target.HTML; + public Quoter quoter = defaultQuoter; + private final Parser parser; + + public BBCode(String text) { + this.parser = new Parser(text); + } + + public String parse() { + StringBuilder sb = new StringBuilder(); + StringBuilder text = new StringBuilder(); + while( !parser.endOfInput() ) { + String block = parseBlock(); + if( block != null ) { + sb.append( textToString(text) ); + sb.append(block); + } else { + text.append( parser.currentChar() ); + parser.anyChar(); + } + } + sb.append( textToString(text) ); + return sb.toString(); + } + + private String parseWellFormed() { + StringBuilder sb = new StringBuilder(); + StringBuilder text = new StringBuilder(); + while( !parser.endOfInput() ) { + String block = parseBlock(); + if( block != null ) { + sb.append( textToString(text) ); + sb.append(block); + continue; + } + if( couldBeTag() ) + break; + text.append( parser.currentChar() ); + parser.anyChar(); + } + sb.append( textToString(text) ); + return sb.toString(); + } + + private String textToString(StringBuilder text) { + String s = text.toString(); + text.setLength(0); + if( target == Target.HTML ) + s = htmlEncode(s); + return s; + } + + private boolean couldBeTag() { + if( parser.currentChar() != '[' ) + return false; + return parser.testIgnoreCase("[b]") + || parser.testIgnoreCase("[/b]") + || parser.testIgnoreCase("[i]") + || parser.testIgnoreCase("[/i]") + || parser.testIgnoreCase("[u]") + || parser.testIgnoreCase("[/u]") + || parser.testIgnoreCase("[url]") + || parser.testIgnoreCase("[url=") + || parser.testIgnoreCase("[/url]") + || parser.testIgnoreCase("[code]") + || parser.testIgnoreCase("[/code]") + || parser.testIgnoreCase("[img]") + || parser.testIgnoreCase("[/img]") + || parser.testIgnoreCase("[color=") + || parser.testIgnoreCase("[/color]") + || parser.testIgnoreCase("[size=") + || parser.testIgnoreCase("[/size]") + || parser.testIgnoreCase("[youtube]") + || parser.testIgnoreCase("[/youtube]") + || parser.testIgnoreCase("[quote]") + || parser.testIgnoreCase("[quote=") + || parser.testIgnoreCase("[/quote]") + ; + } + + private String parseBlock() { + if( parser.currentChar() != '[' ) + return null; + String s; + s = parseB(); if(s!=null) return s; + s = parseI(); if(s!=null) return s; + s = parseU(); if(s!=null) return s; + s = parseUrl1(); if(s!=null) return s; + s = parseUrl2(); if(s!=null) return s; + s = parseCode(); if(s!=null) return s; + s = parseImg(); if(s!=null) return s; + s = parseColor(); if(s!=null) return s; + s = parseSize(); if(s!=null) return s; + s = parseYouTube(); if(s!=null) return s; + s = parseQuote1(); if(s!=null) return s; + s = parseQuote2(); if(s!=null) return s; + return null; + } + + private String parseB() { + parser.begin(); + if( !parser.matchIgnoreCase("[b]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/b]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<b>"+content+"</b>" : content; + return parser.success(rtn); + } + + private String parseI() { + parser.begin(); + if( !parser.matchIgnoreCase("[i]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/i]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<i>"+content+"</i>" : content; + return parser.success(rtn); + } + + private String parseU() { + parser.begin(); + if( !parser.matchIgnoreCase("[u]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/u]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<u>"+content+"</u>" : content; + return parser.success(rtn); + } + + private String parseUrl1() { + parser.begin(); + if( !parser.matchIgnoreCase("[url]") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.matchIgnoreCase("[/url]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<a href='"+url+"'>"+url+"</a>" : url; + return parser.success(rtn); + } + + private String parseUrl2() { + parser.begin(); + if( !parser.matchIgnoreCase("[url=") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/url]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<a href='"+url+"'>"+content+"</a>" : content; + return parser.success(rtn); + } + + private String parseRealUrl() { + parser.begin(); + while( parser.match(' ') ); + int start = parser.currentIndex(); + if( !parser.matchIgnoreCase("http") ) + return parser.failure(null); + parser.matchIgnoreCase("s"); + if( !parser.matchIgnoreCase("://") ) + return parser.failure(null); + while( parser.noneOf(" []'") ); + String url = parser.textFrom(start); + while( parser.match(' ') ); + return parser.success(url); + } + + private String parseCode() { + parser.begin(); + if( !parser.matchIgnoreCase("[code]") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( !parser.testIgnoreCase("[/code]") ) { + if( !parser.anyChar() ) + return parser.failure(null); + } + String content = parser.textFrom(start); + if( !parser.matchIgnoreCase("[/code]") ) throw new RuntimeException(); + String rtn = target==Target.HTML ? "<code>"+content+"</code>" : content; + return parser.success(rtn); + } + + private String parseImg() { + parser.begin(); + if( !parser.matchIgnoreCase("[img]") ) + return parser.failure(null); + String url = parseRealUrl(); + if( !parser.matchIgnoreCase("[/img]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<img src='"+url+"'>" : ""; + return parser.success(rtn); + } + + private String parseColor() { + parser.begin(); + if( !parser.matchIgnoreCase("[color=") ) + return parser.failure(null); + int start = parser.currentIndex(); + parser.match('#'); + while( parser.inCharRange('0','9') + || parser.inCharRange('a','z') + || parser.inCharRange('A','Z') + ); + String color = parser.textFrom(start); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/color]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<span style='color: "+color+"'>"+content+"</span>" : content; + return parser.success(rtn); + } + + private String parseSize() { + parser.begin(); + if( !parser.matchIgnoreCase("[size=") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( parser.match('.') || parser.inCharRange('0','9') ); + String size = parser.textFrom(start); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/size]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<span style='font-size: "+size+"em'>"+content+"</span>" : content; + return parser.success(rtn); + } + + private String parseYouTube() { + parser.begin(); + if( !parser.matchIgnoreCase("[youtube]") ) + return parser.failure(null); + int start = parser.currentIndex(); + while( parser.inCharRange('0','9') + || parser.inCharRange('a','z') + || parser.inCharRange('A','Z') + || parser.match('-') + || parser.match('_') + ); + String id = parser.textFrom(start); + if( id.length()==0 || !parser.matchIgnoreCase("[/youtube]") ) + return parser.failure(null); + String rtn = target==Target.HTML ? "<iframe width='420' height='315' src='https://www.youtube.com/embed/"+id+"' frameborder='0' allowfullscreen></iframe>" : ""; + return parser.success(rtn); + } + + private String parseQuote1() { + parser.begin(); + if( !parser.matchIgnoreCase("[quote]") ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/quote]") ) + return parser.failure(null); + String rtn = quoter.quote(target,content,null); + return parser.success(rtn); + } + + private String parseQuote2() { + parser.begin(); + if( !parser.matchIgnoreCase("[quote=") ) + return parser.failure(null); + List args = new ArrayList(); + int start = parser.currentIndex(); + while( parser.noneOf("[]") ); + String name = parser.textFrom(start).trim(); + if( !parser.match(']') ) + return parser.failure(null); + String content = parseWellFormed(); + if( !parser.matchIgnoreCase("[/quote]") ) + return parser.failure(null); + String rtn = quoter.quote(target,content,name); + return parser.success(rtn); + } + + public static String htmlEncode(String s) { + final char[] a = s.toCharArray(); + StringBuilder buf = new StringBuilder(); + for( char c : a ) { + switch(c) { + case '&': + buf.append("&"); + break; + case '<': + buf.append("<"); + break; + case '>': + buf.append(">"); + break; + case '"': + buf.append("""); + break; + default: + buf.append(c); + } + } + return buf.toString(); + } + +}
--- a/src/luan/modules/Parsers.luan Tue Jun 28 17:59:19 2022 +0300 +++ b/src/luan/modules/Parsers.luan Thu Jun 30 20:04:34 2022 -0600 @@ -1,5 +1,5 @@ require "java" -local BBCode = require "java:luan.modules.parsers.BBCode" +local BBCodeLuan = require "java:luan.modules.parsers.BBCodeLuan" local Csv = require "java:luan.modules.parsers.Csv" local Theme = require "java:luan.modules.parsers.Theme" local Xml = require "java:luan.modules.parsers.Xml" @@ -9,8 +9,8 @@ local Parsers = {} -Parsers.bbcode_to_html = BBCode.toHtml -Parsers.bbcode_to_text = BBCode.toText +Parsers.bbcode_to_html = BBCodeLuan.toHtml +Parsers.bbcode_to_text = BBCodeLuan.toText Parsers.csv_to_list = Csv.toList Parsers.json_string = BasicLuan.json_string Parsers.theme_to_luan = Theme.toLuan
--- a/src/luan/modules/parsers/BBCode.java Tue Jun 28 17:59:19 2022 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -package luan.modules.parsers; - -import java.util.List; -import java.util.ArrayList; -import luan.Luan; -import luan.LuanFunction; -import luan.LuanException; -import luan.modules.Utils; -import luan.modules.HtmlLuan; -import goodjava.parser.Parser; - - -public final class BBCode { - - public static String toHtml(Luan luan,String bbcode,LuanFunction quoter) throws LuanException { - return new BBCode(luan,bbcode,quoter,true).parse(); - } - - public static String toText(Luan luan,String bbcode,LuanFunction quoter) throws LuanException { - return new BBCode(luan,bbcode,quoter,false).parse(); - } - - private final Luan luan; - private final Parser parser; - private final LuanFunction quoter; - private final boolean toHtml; - - private BBCode(Luan luan,String text,LuanFunction quoter,boolean toHtml) throws LuanException { - Utils.checkNotNull(text,1); -// Utils.checkNotNull(quoter,2); - this.luan = luan; - this.parser = new Parser(text); - this.quoter = quoter; - this.toHtml = toHtml; - } - - private String parse() throws LuanException { - StringBuilder sb = new StringBuilder(); - StringBuilder text = new StringBuilder(); - while( !parser.endOfInput() ) { - String block = parseBlock(); - if( block != null ) { - sb.append( textToString(text) ); - sb.append(block); - } else { - text.append( parser.currentChar() ); - parser.anyChar(); - } - } - sb.append( textToString(text) ); - return sb.toString(); - } - - private String parseWellFormed() throws LuanException { - StringBuilder sb = new StringBuilder(); - StringBuilder text = new StringBuilder(); - while( !parser.endOfInput() ) { - String block = parseBlock(); - if( block != null ) { - sb.append( textToString(text) ); - sb.append(block); - continue; - } - if( couldBeTag() ) - break; - text.append( parser.currentChar() ); - parser.anyChar(); - } - sb.append( textToString(text) ); - return sb.toString(); - } - - private String textToString(StringBuilder text) throws LuanException { - String s = text.toString(); - text.setLength(0); - if( toHtml ) - s = HtmlLuan.encode(s); - return s; - } - - private boolean couldBeTag() { - if( parser.currentChar() != '[' ) - return false; - return parser.testIgnoreCase("[b]") - || parser.testIgnoreCase("[/b]") - || parser.testIgnoreCase("[i]") - || parser.testIgnoreCase("[/i]") - || parser.testIgnoreCase("[u]") - || parser.testIgnoreCase("[/u]") - || parser.testIgnoreCase("[url]") - || parser.testIgnoreCase("[url=") - || parser.testIgnoreCase("[/url]") - || parser.testIgnoreCase("[code]") - || parser.testIgnoreCase("[/code]") - || parser.testIgnoreCase("[img]") - || parser.testIgnoreCase("[/img]") - || parser.testIgnoreCase("[color=") - || parser.testIgnoreCase("[/color]") - || parser.testIgnoreCase("[size=") - || parser.testIgnoreCase("[/size]") - || parser.testIgnoreCase("[youtube]") - || parser.testIgnoreCase("[/youtube]") - || parser.testIgnoreCase("[quote]") - || parser.testIgnoreCase("[quote=") - || parser.testIgnoreCase("[/quote]") - ; - } - - private String parseBlock() throws LuanException { - if( parser.currentChar() != '[' ) - return null; - String s; - s = parseB(); if(s!=null) return s; - s = parseI(); if(s!=null) return s; - s = parseU(); if(s!=null) return s; - s = parseUrl1(); if(s!=null) return s; - s = parseUrl2(); if(s!=null) return s; - s = parseCode(); if(s!=null) return s; - s = parseImg(); if(s!=null) return s; - s = parseColor(); if(s!=null) return s; - s = parseSize(); if(s!=null) return s; - s = parseYouTube(); if(s!=null) return s; - s = parseQuote1(); if(s!=null) return s; - s = parseQuote2(); if(s!=null) return s; - return null; - } - - private String parseB() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[b]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/b]") ) - return parser.failure(null); - String rtn = toHtml ? "<b>"+content+"</b>" : content; - return parser.success(rtn); - } - - private String parseI() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[i]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/i]") ) - return parser.failure(null); - String rtn = toHtml ? "<i>"+content+"</i>" : content; - return parser.success(rtn); - } - - private String parseU() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[u]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/u]") ) - return parser.failure(null); - String rtn = toHtml ? "<u>"+content+"</u>" : content; - return parser.success(rtn); - } - - private String parseUrl1() { - parser.begin(); - if( !parser.matchIgnoreCase("[url]") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.matchIgnoreCase("[/url]") ) - return parser.failure(null); - String rtn = toHtml ? "<a href='"+url+"'>"+url+"</u>" : url; - return parser.success(rtn); - } - - private String parseUrl2() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[url=") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/url]") ) - return parser.failure(null); - String rtn = toHtml ? "<a href='"+url+"'>"+content+"</u>" : content; - return parser.success(rtn); - } - - private String parseRealUrl() { - parser.begin(); - while( parser.match(' ') ); - int start = parser.currentIndex(); - if( !parser.matchIgnoreCase("http") ) - return parser.failure(null); - parser.matchIgnoreCase("s"); - if( !parser.matchIgnoreCase("://") ) - return parser.failure(null); - while( parser.noneOf(" []'") ); - String url = parser.textFrom(start); - while( parser.match(' ') ); - return parser.success(url); - } - - private String parseCode() { - parser.begin(); - if( !parser.matchIgnoreCase("[code]") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( !parser.testIgnoreCase("[/code]") ) { - if( !parser.anyChar() ) - return parser.failure(null); - } - String content = parser.textFrom(start); - if( !parser.matchIgnoreCase("[/code]") ) throw new RuntimeException(); - String rtn = toHtml ? "<code>"+content+"</code>" : content; - return parser.success(rtn); - } - - private String parseImg() { - parser.begin(); - if( !parser.matchIgnoreCase("[img]") ) - return parser.failure(null); - String url = parseRealUrl(); - if( !parser.matchIgnoreCase("[/img]") ) - return parser.failure(null); - String rtn = toHtml ? "<img src='"+url+"'>" : ""; - return parser.success(rtn); - } - - private String parseColor() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[color=") ) - return parser.failure(null); - int start = parser.currentIndex(); - parser.match('#'); - while( parser.inCharRange('0','9') - || parser.inCharRange('a','z') - || parser.inCharRange('A','Z') - ); - String color = parser.textFrom(start); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/color]") ) - return parser.failure(null); - String rtn = toHtml ? "<span style='color: "+color+"'>"+content+"</span>" : content; - return parser.success(rtn); - } - - private String parseSize() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[size=") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( parser.match('.') || parser.inCharRange('0','9') ); - String size = parser.textFrom(start); - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/size]") ) - return parser.failure(null); - String rtn = toHtml ? "<span style='font-size: "+size+"em'>"+content+"</span>" : content; - return parser.success(rtn); - } - - private String parseYouTube() { - parser.begin(); - if( !parser.matchIgnoreCase("[youtube]") ) - return parser.failure(null); - int start = parser.currentIndex(); - while( parser.inCharRange('0','9') - || parser.inCharRange('a','z') - || parser.inCharRange('A','Z') - || parser.match('-') - || parser.match('_') - ); - String id = parser.textFrom(start); - if( id.length()==0 || !parser.matchIgnoreCase("[/youtube]") ) - return parser.failure(null); - String rtn = toHtml ? "<iframe width='420' height='315' src='https://www.youtube.com/embed/"+id+"' frameborder='0' allowfullscreen></iframe>" : ""; - return parser.success(rtn); - } - - private String quote(Object... args) throws LuanException { - if( quoter==null ) { - if( toHtml ) - throw new LuanException("BBCode quoter function not defined"); - else - return ""; - } - Object obj = quoter.call(luan,args); - if( !(obj instanceof String) ) - throw new LuanException("BBCode quoter function returned "+Luan.type(obj)+" but string required"); - return (String)obj; - } - - private String parseQuote1() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[quote]") ) - return parser.failure(null); - String content = parseWellFormed(); - if( !parser.matchIgnoreCase("[/quote]") ) - return parser.failure(null); - String rtn = quote(content); - return parser.success(rtn); - } - - private String parseQuote2() throws LuanException { - parser.begin(); - if( !parser.matchIgnoreCase("[quote=") ) - return parser.failure(null); - List args = new ArrayList(); - int start = parser.currentIndex(); - while( parser.noneOf("[];") ); - String name = parser.textFrom(start).trim(); - if( name.length() == 0 ) - return parser.failure(null); - args.add(name); - while( parser.match(';') ) { - start = parser.currentIndex(); - while( parser.noneOf("[];'") ); - String src = parser.textFrom(start).trim(); - args.add(src); - } - if( !parser.match(']') ) - return parser.failure(null); - String content = parseWellFormed(); - args.add(0,content); - if( !parser.matchIgnoreCase("[/quote]") ) - return parser.failure(null); - String rtn = quote(args.toArray()); - return parser.success(rtn); - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/BBCodeLuan.java Thu Jun 30 20:04:34 2022 -0600 @@ -0,0 +1,54 @@ +package luan.modules.parsers; + +import java.util.List; +import java.util.ArrayList; +import luan.Luan; +import luan.LuanFunction; +import luan.LuanException; +import luan.LuanRuntimeException; +import luan.modules.Utils; +import luan.modules.HtmlLuan; +import goodjava.bbcode.BBCode; + + +public final class BBCodeLuan { + + private static BBCode.Quoter quoter(final Luan luan,final LuanFunction quoterFn) { + return new BBCode.Quoter() { + public String quote(BBCode.Target target,String text,String param) { + try { + Object obj = quoterFn.call(luan,text,param); + if( !(obj instanceof String) ) + throw new LuanException("BBCode quoter function returned "+Luan.type(obj)+" but string required"); + return (String)obj; + } catch(LuanException e) { + throw new LuanRuntimeException(e); + } + } + }; + } + + public static String toHtml(Luan luan,String text,LuanFunction quoterFn) throws LuanException { + return parse(luan,text,quoterFn,BBCode.Target.HTML); + } + + public static String toText(Luan luan,String text,LuanFunction quoterFn) throws LuanException { + return parse(luan,text,quoterFn,BBCode.Target.TEXT); + } + + private static String parse(Luan luan,String text,LuanFunction quoterFn,BBCode.Target target) + throws LuanException + { + Utils.checkNotNull(text,1); + BBCode bbcode = new BBCode(text); + bbcode.target = target; + if( quoterFn != null ) + bbcode.quoter = quoter(luan,quoterFn); + try { + return bbcode.parse(); + } catch(LuanRuntimeException e) { + throw (LuanException)e.getCause(); + } + } + +}