Mercurial Hosting > luan
view src/goodjava/bbcode/BBCode.java @ 1712:36c28be6d432
improve html and bbcode
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 14 Jul 2022 22:14:21 -0600 |
parents | 05d14db623b6 |
children | 31a82b0d0a87 |
line wrap: on
line source
package goodjava.bbcode; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.LinkedHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import goodjava.parser.Parser; public final class BBCode { private static final Pattern tagPtn = Pattern.compile( "\\[(/?[a-zA-Z]+(=[^ \\n\\t\\[\\]]*)?)\\]" ); public static String encode(String s) { return tagPtn.matcher(s).replaceAll("[brackets]$1[/brackets]"); } public final class Element { public final String name; public final String param; public final Object contents; // String, Element, or List public final Map<String,String> extra; private Element(String name,Object contents) { this(name,null,contents); } private Element(String name,String param,Object contents) { this(name,param,contents,null); } private Element(String name,String param,Object contents,Map<String,String> extra) { this.name = name.toLowerCase(); this.param = param; this.contents = contents; this.extra = extra; } } public static Object parse(String text) { if( text.indexOf('[') == -1 ) return text; return new BBCode(text).parse(); } private final Parser parser; private BBCode(String text) { this.parser = new Parser(text); } private Object parse() { List list = new ArrayList(); StringBuilder text = new StringBuilder(); while( !parser.endOfInput() ) { Element block = parseBlock(false); if( block != null ) { add(list,text); list.add( block ); } else { text.append( parser.currentChar() ); parser.anyChar(); } } add(list,text); return list.size()==1 ? list.get(0) : list; } private Object parseUntil(String end) { return parseUntil(end,false); } private Object parseUntil(String end,boolean inList) { List list = new ArrayList(); StringBuilder text = new StringBuilder(); while( !parser.matchIgnoreCase(end) ) { if( parser.endOfInput() ) return null; Element block = parseBlock(inList); if( block != null ) { add(list,text); list.add( block ); } else { text.append( parser.currentChar() ); parser.anyChar(); } } add(list,text); return list.size()==1 ? list.get(0) : list; } private void add(List list,StringBuilder text) { if( text.length() > 0 ) { list.add( text.toString() ); text.setLength(0); } } private Element parseBlock(boolean inList) { if( parser.currentChar() != '[' ) return null; Element s; s = parseB(); if(s!=null) return s; s = parseI(); if(s!=null) return s; s = parseU(); if(s!=null) return s; s = parseS(); if(s!=null) return s; s = parseSup(); if(s!=null) return s; s = parseBrackets(); if(s!=null) return s; s = parseUrl(); 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 = parseVideo(); if(s!=null) return s; s = parseQuote(); if(s!=null) return s; s = parseUl(); if(s!=null) return s; s = parseOl(); if(s!=null) return s; if( inList ) { s = parseLi(); if(s!=null) return s; } return null; } private Element parseB() { parser.begin(); if( !parser.matchIgnoreCase("[b]") ) return parser.failure(null); Object content = parseUntil("[/b]"); if( content==null ) return parser.failure(null); Element rtn = new Element("b",content); return parser.success(rtn); } private Element parseI() { parser.begin(); if( !parser.matchIgnoreCase("[i]") ) return parser.failure(null); Object content = parseUntil("[/i]"); if( content==null ) return parser.failure(null); Element rtn = new Element("i",content); return parser.success(rtn); } private Element parseU() { parser.begin(); if( !parser.matchIgnoreCase("[u]") ) return parser.failure(null); Object content = parseUntil("[/u]"); if( content==null ) return parser.failure(null); Element rtn = new Element("u",content); return parser.success(rtn); } private Element parseS() { parser.begin(); if( !parser.matchIgnoreCase("[s]") ) return parser.failure(null); Object content = parseUntil("[/s]"); if( content==null ) return parser.failure(null); Element rtn = new Element("s",content); return parser.success(rtn); } private Element parseSup() { parser.begin(); if( !parser.matchIgnoreCase("[sup]") ) return parser.failure(null); Object content = parseUntil("[/sup]"); if( content==null ) return parser.failure(null); Element rtn = new Element("sup",content); return parser.success(rtn); } private Element parseBrackets() { parser.begin(); if( !parser.matchIgnoreCase("[brackets]") ) return parser.failure(null); Object content = parseUntil("[/brackets]"); if( content==null ) return parser.failure(null); Element rtn = new Element("brackets",content); return parser.success(rtn); } private Element parseUrl() { parser.begin(); if( !parser.matchIgnoreCase("[url") ) return parser.failure(null); String url; Object content; if( parser.match(']') ) { url = parseRealUrl(); content = null; if( !parser.matchIgnoreCase("[/url]") ) return parser.failure(null); } else if( parser.match('=') ) { url = parseRealUrl(); if( !parser.match(']') ) return parser.failure(null); content = parseUntil("[/url]"); if( content==null ) return parser.failure(null); } else return parser.failure(null); Element rtn = new Element("url",url,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(" \n\t[]") ); String url = parser.textFrom(start); while( parser.match(' ') ); return parser.success(url); } private Element parseCode() { parser.begin(); String param; String end; if( !parser.matchIgnoreCase("[code") ) return parser.failure(null); if( parser.match(']') ) { param = null; end = "[/code]"; } else if( parser.match('=') ) { int start = parser.currentIndex(); while( parser.noneOf("[]\n") ); param = parser.textFrom(start); if( !parser.match(']') ) return parser.failure(null); end = "[/code=" + param + "]"; } else return parser.failure(null); int start = parser.currentIndex(); while( !parser.testIgnoreCase(end) ) { if( !parser.anyChar() ) return parser.failure(null); } String content = parser.textFrom(start); if( !parser.matchIgnoreCase(end) ) throw new RuntimeException(); Element rtn = new Element("code",param,content); return parser.success(rtn); } private Element parseImg() { parser.begin(); if( !parser.matchIgnoreCase("[img]") ) return parser.failure(null); String url = parseRealUrl(); if( !parser.matchIgnoreCase("[/img]") ) return parser.failure(null); Element rtn = new Element("img",url); return parser.success(rtn); } private Element 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); Object content = parseUntil("[/color]"); if( content==null ) return parser.failure(null); Element rtn = new Element("color",color,content); return parser.success(rtn); } private Element 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); Object content = parseUntil("[/size]"); if( content==null ) return parser.failure(null); Element rtn = new Element("size",size,content); return parser.success(rtn); } private static final Pattern youtubePtn1 = Pattern.compile("https://youtu.be/([a-zA-Z0-9_-]+)(?:\\?t=([0-9]+))?"); private static final Pattern youtubePtn2 = Pattern.compile("https://www.youtube.com/watch?v=([a-zA-Z0-9_-]+)(?:\\?t=([0-9]+)s)?"); private static final Pattern bitchutePtn = Pattern.compile("https://www.bitchute.com/video/([a-zA-Z0-9]+)/"); private static Matcher find(Pattern ptn,String s) { Matcher m = ptn.matcher(s); return m.find() ? m : null; } private Element parseVideo() { parser.begin(); if( !parser.matchIgnoreCase("[video]") ) return parser.failure(null); String url = parseRealUrl(); if( !parser.matchIgnoreCase("[/video]") ) return parser.failure(null); Map<String,String> extra = new LinkedHashMap<String,String>(); Element rtn = new Element("video",null,url,extra); Matcher m; m = find(youtubePtn1,url); if( m == null ) m = find(youtubePtn2,url); if( m != null ) { extra.put( "site", "youtube" ); extra.put( "id", m.group(1) ); String t = m.group(2); if( t != null ) extra.put( "start", t ); return parser.success(rtn); } m = find(bitchutePtn,url); if( m != null ) { extra.put( "site", "bitchute" ); extra.put( "id", m.group(1) ); return parser.success(rtn); } return parser.success(rtn); } private Element parseQuote() { parser.begin(); if( !parser.matchIgnoreCase("[quote") ) return parser.failure(null); String name; if( parser.match(']') ) { name = null; } else if( parser.match('=') ) { int start = parser.currentIndex(); while( parser.noneOf("[]\n") ); name = parser.textFrom(start); if( !parser.match(']') ) return parser.failure(null); } else return parser.failure(null); Object content = parseUntil("[/quote]"); if( content==null ) return parser.failure(null); Element rtn = new Element("quote",name,content); return parser.success(rtn); } private Element parseUl() { parser.begin(); if( !parser.matchIgnoreCase("[ul]") ) return parser.failure(null); Object content = parseUntil("[/ul]",true); if( content==null ) return parser.failure(null); Element rtn = new Element("ul",content); return parser.success(rtn); } private Element parseOl() { parser.begin(); if( !parser.matchIgnoreCase("[ol]") ) return parser.failure(null); Object content = parseUntil("[/ol]",true); if( content==null ) return parser.failure(null); Element rtn = new Element("ol",content); return parser.success(rtn); } private Element parseLi() { parser.begin(); if( !parser.matchIgnoreCase("[li]") ) return parser.failure(null); Object content = parseUntil("[/li]"); if( content==null ) return parser.failure(null); Element rtn = new Element("li",content); return parser.success(rtn); } }