/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.message.boards.parser.bbcode.internal; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.IntegerWrapper; import com.liferay.portal.kernel.util.SetUtil; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author Iliyan Peychev */ public class BBCodeParser { public static final int TYPE_DATA = 0x04; public static final int TYPE_TAG_END = 0x02; public static final int TYPE_TAG_START = 0x01; public static final int TYPE_TAG_START_END = TYPE_TAG_START | TYPE_TAG_END; public BBCodeParser() { _blockElements = SetUtil.fromArray( new String[] { "*", "center", "code", "justify", "left", "li", "list", "q", "quote", "right", "table", "td", "th", "tr" }); _inlineElements = SetUtil.fromArray( new String[] { "b", "color", "font", "i", "img", "s", "size", "u", "url" }); _selfCloseElements = SetUtil.fromArray(new String[] {"*"}); } public List<BBCodeItem> parse(String text) { List<BBCodeItem> bbCodeItems = new ArrayList<>(); BBCodeLexer bbCodeLexer = new BBCodeLexer(text); Stack<String> tags = new Stack<>(); IntegerWrapper marker = new IntegerWrapper(); BBCodeToken bbCodeToken = null; while ((bbCodeToken = bbCodeLexer.getNextBBCodeToken()) != null) { handleData(bbCodeItems, bbCodeLexer, marker, bbCodeToken, text); if (bbCodeToken.getStartTag() == null) { handleTagEnd(bbCodeItems, tags, bbCodeToken); continue; } handleTagStart(bbCodeItems, tags, bbCodeToken); String startTag = bbCodeToken.getStartTag(); if (!startTag.equals("code")) { continue; } while (true) { bbCodeToken = bbCodeLexer.getNextBBCodeToken(); if (bbCodeToken == null) { break; } String endTag = GetterUtil.getString(bbCodeToken.getEndTag()); if (endTag.equals("code")) { break; } } handleData(bbCodeItems, bbCodeLexer, marker, bbCodeToken, text); if (bbCodeToken != null) { handleTagEnd(bbCodeItems, tags, bbCodeToken); } else { break; } } handleData(bbCodeItems, bbCodeLexer, marker, null, text); handleTagEnd(bbCodeItems, tags, null); return bbCodeItems; } protected void handleData( List<BBCodeItem> bbCodeItems, BBCodeLexer bbCodeLexer, IntegerWrapper marker, BBCodeToken bbCodeToken, String data) { int length = data.length(); int lastIndex = length; if (bbCodeToken != null) { lastIndex = bbCodeLexer.getLastIndex(); length = lastIndex; String tag = bbCodeToken.getStartTag(); if (tag == null) { tag = bbCodeToken.getEndTag(); } if (isValidTag(tag)) { length = bbCodeToken.getStart(); } } if (length > marker.getValue()) { BBCodeItem bbCodeItem = new BBCodeItem( TYPE_DATA, null, data.substring(marker.getValue(), length)); bbCodeItems.add(bbCodeItem); } marker.setValue(lastIndex); } protected void handleTagEnd( List<BBCodeItem> bbCodeItems, Stack<String> tags, BBCodeToken bbCodeToken) { String endTag = null; int size = 0; if (bbCodeToken != null) { endTag = bbCodeToken.getEndTag(); for (size = tags.size() - 1; size >= 0; size--) { if (endTag.equals(tags.elementAt(size))) { break; } } } if (size >= 0) { for (int i = tags.size() - 1; i >= size; i--) { BBCodeItem bbCodeItem = new BBCodeItem( TYPE_TAG_END, null, tags.elementAt(i)); bbCodeItems.add(bbCodeItem); } tags.setSize(size); } } protected void handleTagStart( List<BBCodeItem> bbCodeItems, Stack<String> tags, BBCodeToken bbCodeToken) { String startTag = bbCodeToken.getStartTag(); if (!isValidTag(startTag)) { return; } if (!tags.isEmpty()) { if (_blockElements.contains(startTag)) { String currentTag = null; while (!tags.isEmpty() && ((currentTag = tags.lastElement()) != null) && _inlineElements.contains(currentTag)) { BBCodeToken currentTagBBCodeToken = new BBCodeToken( currentTag); handleTagEnd(bbCodeItems, tags, currentTagBBCodeToken); } } if (!tags.isEmpty() && _selfCloseElements.contains(startTag) && startTag.equals(tags.lastElement())) { BBCodeToken tagBBCodeToken = new BBCodeToken(startTag); handleTagEnd(bbCodeItems, tags, tagBBCodeToken); } } tags.push(startTag); BBCodeItem bbCodeItem = new BBCodeItem( TYPE_TAG_START, bbCodeToken.getAttribute(), startTag); bbCodeItems.add(bbCodeItem); } protected boolean isValidTag(String tag) { if ((tag != null) && (tag.length() > 0)) { Matcher matcher = _tagPattern.matcher(tag); return matcher.matches(); } return false; } private final Set<String> _blockElements; private final Set<String> _inlineElements; private final Set<String> _selfCloseElements; private final Pattern _tagPattern = Pattern.compile( "^/?(?:b|center|code|colou?r|email|font|i|img|justify|left|li|list" + "|pre|q|quote|right|s|size|table|td|th|tr|u|url|\\*)$", Pattern.CASE_INSENSITIVE); }