/**
* Copyright (C) 2009 eXo Platform SAS.
*
* This 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 software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.html.parser;
import org.exoplatform.services.html.HTMLNode;
import org.exoplatform.services.html.MoveType;
import org.exoplatform.services.html.Name;
import org.exoplatform.services.html.NodeConfig;
import org.exoplatform.services.html.NodeConfigs;
import org.exoplatform.services.html.Tag;
import org.exoplatform.services.html.Group.Block;
import org.exoplatform.services.html.Group.Default;
import org.exoplatform.services.html.Group.Flow;
import org.exoplatform.services.html.Group.Fontstyle;
import org.exoplatform.services.html.Group.Formctrl;
import org.exoplatform.services.html.Group.HeadContent;
import org.exoplatform.services.html.Group.HeadMisc;
import org.exoplatform.services.html.Group.Heading;
import org.exoplatform.services.html.Group.HtmlContent;
import org.exoplatform.services.html.Group.List;
import org.exoplatform.services.html.Group.Phrase;
import org.exoplatform.services.html.Group.Preformatted;
import org.exoplatform.services.html.Group.Special;
import org.exoplatform.services.html.Group.Table;
import org.exoplatform.services.token.TypeToken;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
/**
* Author : Nhu Dinh Thuan
* Email:nhudinhthuan@yahoo.com
* Aug 3, 2006
*/
@NodeConfigs({
@NodeConfig(name = Name.A, type = Special.class),
@NodeConfig(name = Name.ABBR, type = Phrase.class),
@NodeConfig(name = Name.ACRONYM, type = Phrase.class),
@NodeConfig(name = Name.ADDRESS, type = Block.class),
@NodeConfig(name = Name.APPLET, type = Special.class),
@NodeConfig(name = Name.AREA, end = Tag.FORBIDDEN, type = Default.class),
@NodeConfig(name = Name.B, type = Fontstyle.class),
@NodeConfig(name = Name.BASE, end = Tag.FORBIDDEN, parent = {Name.HEAD}, type = HeadContent.class),
@NodeConfig(name = Name.BASEFONT, end = Tag.FORBIDDEN, type = Special.class),
@NodeConfig(name = Name.BDO, type = Special.class),
@NodeConfig(name = Name.BIG, type = Fontstyle.class),
@NodeConfig(name = Name.BLOCKQUOTE, type = Block.class),
@NodeConfig(name = Name.BODY, start = Tag.OPTIONAL, end = Tag.OPTIONAL, parent = {Name.HTML}, type = HtmlContent.class, only = true),
@NodeConfig(name = Name.BR, end = Tag.FORBIDDEN, type = Special.class),
@NodeConfig(name = Name.BUTTON, type = Formctrl.class),
@NodeConfig(name = Name.CAPTION, parent = {Name.TABLE}, type = Table.class),
@NodeConfig(name = Name.CENTER, type = Block.class),
@NodeConfig(name = Name.CITE, type = Phrase.class),
@NodeConfig(name = Name.CODE, type = Phrase.class),
@NodeConfig(name = Name.COL, end = Tag.FORBIDDEN, parent = {Name.COLGROUP}, type = Default.class),
@NodeConfig(name = Name.COLGROUP, end = Tag.OPTIONAL, parent = {Name.TABLE}, children = {Name.COL}, type = Default.class),
@NodeConfig(name = Name.DD, end = Tag.OPTIONAL, parent = {Name.DL}, type = Default.class, end_names = {Name.DT,
Name.DD}),
@NodeConfig(name = Name.DEL, type = Default.class),
@NodeConfig(name = Name.DFN, type = Phrase.class),
@NodeConfig(name = Name.DIR, type = List.class),
@NodeConfig(name = Name.DIV, type = Block.class),
@NodeConfig(name = Name.DL, type = Block.class),
@NodeConfig(name = Name.DT, end = Tag.OPTIONAL, parent = {Name.DL}, type = Default.class, end_names = {Name.DT}),
@NodeConfig(name = Name.EM, type = Phrase.class),
@NodeConfig(name = Name.EMBED, end = Tag.FORBIDDEN, parent = {Name.OBJECT}, type = Default.class),
@NodeConfig(name = Name.FIELDSET, type = Block.class),
@NodeConfig(name = Name.FONT, type = Special.class),
@NodeConfig(name = Name.FORM, hidden = true, type = Block.class),
@NodeConfig(name = Name.FRAME, end = Tag.FORBIDDEN, parent = {Name.FRAMESET}, type = Default.class),
@NodeConfig(name = Name.FRAMESET, children = {Name.FRAMESET, Name.FRAME, Name.NOFRAMES}, type = Default.class),
@NodeConfig(name = Name.H1, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.H2, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.H3, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.H4, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.H5, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.H6, end = Tag.OPTIONAL, type = Heading.class, end_types = {Heading.class}),
@NodeConfig(name = Name.HEAD, start = Tag.OPTIONAL, end = Tag.OPTIONAL, parent = {Name.HTML}, children = {
Name.TITLE, Name.META, Name.LINK, Name.SCRIPT, Name.STYLE, Name.BASE}, type = HtmlContent.class, only = true),
@NodeConfig(name = Name.HR, end = Tag.FORBIDDEN, type = Block.class),
@NodeConfig(name = Name.HTML, start = Tag.OPTIONAL, end = Tag.OPTIONAL, children = {Name.HEAD, Name.BODY}, type = Default.class, only = true),
@NodeConfig(name = Name.I, type = Fontstyle.class),
@NodeConfig(name = Name.IFRAME, type = Special.class),
@NodeConfig(name = Name.IMG, end = Tag.FORBIDDEN, type = Special.class),
@NodeConfig(name = Name.INPUT, end = Tag.FORBIDDEN, type = Formctrl.class),
@NodeConfig(name = Name.INS, type = Default.class),
@NodeConfig(name = Name.ISINDEX, end = Tag.FORBIDDEN, type = Block.class),
@NodeConfig(name = Name.KBD, type = Phrase.class),
@NodeConfig(name = Name.LABEL, type = Formctrl.class),
@NodeConfig(name = Name.LEGEND, parent = {Name.FIELDSET}, type = Default.class),
@NodeConfig(name = Name.LI, end = Tag.OPTIONAL, type = Flow.class, end_names = {Name.LI}),
@NodeConfig(name = Name.LINK, end = Tag.FORBIDDEN, parent = {Name.HEAD}, type = HeadMisc.class),
@NodeConfig(name = Name.MAP, type = Special.class),
@NodeConfig(name = Name.MARQUEE, type = Block.class),
@NodeConfig(name = Name.MENU, type = List.class),
@NodeConfig(name = Name.META, end = Tag.FORBIDDEN, parent = {Name.HEAD}, type = HeadMisc.class),
@NodeConfig(name = Name.NOBR, type = Block.class),
@NodeConfig(name = Name.NOFRAMES, parent = {Name.FRAMESET}, type = Block.class),
@NodeConfig(name = Name.NOSCRIPT, type = Block.class),
@NodeConfig(name = Name.OBJECT, type = Special.class),
@NodeConfig(name = Name.OL, type = List.class),
@NodeConfig(name = Name.OPTGROUP, parent = {Name.SELECT}, children = {Name.OPTION}, type = Table.class, move = MoveType.REMOVE),
@NodeConfig(name = Name.OPTION, end = Tag.OPTIONAL, parent = {Name.SELECT, Name.OPTGROUP}, type = Default.class),
@NodeConfig(name = Name.P, end = Tag.OPTIONAL, type = Block.class, end_types = {Block.class, Heading.class}),
@NodeConfig(name = Name.PARAM, end = Tag.FORBIDDEN, parent = {Name.OBJECT}, type = Default.class),
@NodeConfig(name = Name.PRE, type = Preformatted.class),
@NodeConfig(name = Name.Q, type = Special.class),
@NodeConfig(name = Name.S, type = Fontstyle.class),
@NodeConfig(name = Name.SAMP, type = Phrase.class),
@NodeConfig(name = Name.SCRIPT, type = Special.class),
@NodeConfig(name = Name.SELECT, children = {Name.OPTION, Name.OPTGROUP}, type = Formctrl.class, move = MoveType.REMOVE, block = true),
@NodeConfig(name = Name.SMALL, type = Fontstyle.class),
@NodeConfig(name = Name.SPAN, type = Special.class),
@NodeConfig(name = Name.STRIKE, type = Fontstyle.class),
@NodeConfig(name = Name.STRONG, type = Phrase.class),
@NodeConfig(name = Name.STYLE, move = MoveType.HEADER, type = HeadMisc.class),
@NodeConfig(name = Name.SUB, type = Special.class),
@NodeConfig(name = Name.SUP, type = Special.class),
@NodeConfig(name = Name.TABLE, children = {Name.TBODY, Name.THEAD, Name.TFOOT, Name.CAPTION, Name.COLGROUP}, type = Block.class, block = true, move = MoveType.INSERT),
@NodeConfig(name = Name.TBODY, start = Tag.OPTIONAL, end = Tag.OPTIONAL, parent = {Name.TABLE}, children = {Name.TR}, type = Table.class, move = MoveType.INSERT),
@NodeConfig(name = Name.TD, end = Tag.OPTIONAL, parent = {Name.TR}, type = Table.class),
@NodeConfig(name = Name.TEXTAREA, type = Formctrl.class),
@NodeConfig(name = Name.TFOOT, end = Tag.OPTIONAL, parent = {Name.TABLE}, children = {Name.TR}, type = Table.class, move = MoveType.INSERT),
@NodeConfig(name = Name.TH, end = Tag.OPTIONAL, parent = {Name.TR}, type = Table.class),
@NodeConfig(name = Name.THEAD, end = Tag.OPTIONAL, parent = {Name.TABLE}, children = {Name.TR}, type = Table.class, move = MoveType.INSERT),
@NodeConfig(name = Name.TITLE, move = MoveType.HEADER, type = HeadContent.class),
@NodeConfig(name = Name.TR, end = Tag.OPTIONAL, parent = {Name.TBODY, Name.THEAD, Name.TFOOT}, children = {Name.TD,
Name.TH}, type = Table.class, move = MoveType.INSERT), @NodeConfig(name = Name.TT, type = Fontstyle.class),
@NodeConfig(name = Name.U, type = Fontstyle.class), @NodeConfig(name = Name.UL, type = List.class),
@NodeConfig(name = Name.VAR, type = Phrase.class),
@NodeConfig(name = Name.CONTENT, end = Tag.FORBIDDEN, type = Default.class),
@NodeConfig(name = Name.COMMENT, end = Tag.FORBIDDEN, type = Default.class),
@NodeConfig(name = Name.DOCTYPE, end = Tag.FORBIDDEN, type = Default.class),
@NodeConfig(name = Name.UNKNOWN, end = Tag.FORBIDDEN, type = Default.class)})
public class HTML
{
public static SoftReference<NodeConfig[]> refConfig;
private static HashMap<Name, NodeConfig> mapConfig = new HashMap<Name, NodeConfig>();
static public Name getName(String value)
{
Name name = null;
try
{
name = Name.valueOf(value);
}
catch (Exception e)
{
return null;
}
return name;
}
static public NodeConfig getConfig(String value)
{
Name name = getName(value);
if (name == null)
return null;
return getConfig(name);
}
static public NodeConfig getConfig(Name key)
{
NodeConfig config = mapConfig.get(key);
if (config != null)
return config;
if (refConfig == null || refConfig.get() == null)
loadNodeConfigs();
NodeConfig[] configs = refConfig.get();
int low = 0;
int high = configs.length - 1;
while (low <= high)
{
int mid = (low + high) >> 1;
config = configs[mid];
int cmp = config.name().compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
{
config = configs[mid];
mapConfig.put(key, config);
return config;
}
}
return null;
}
private static void loadNodeConfigs()
{
NodeConfigs nodeConfigs = HTML.class.getAnnotation(NodeConfigs.class);
NodeConfig[] configs = nodeConfigs.value();
Arrays.sort(configs, new Comparator<NodeConfig>()
{
public int compare(NodeConfig c1, NodeConfig c2)
{
return c1.name().compareTo(c2.name());
}
});
refConfig = new SoftReference<NodeConfig[]>(configs);
}
static boolean isChild(HTMLNode parent, NodeConfig config)
{
if (parent.getConfig().children().length < 1 && parent.getConfig().children_types().length < 1)
return true;
if (parent.getConfig().end() == Tag.FORBIDDEN)
return false;
if (config.hidden())
return true;
Name[] names = parent.getConfig().children();
for (Name name : names)
{
if (config.name() == name)
return true;
}
Class<?>[] children = parent.getConfig().children_types();
if (children.length < 1)
return false;
NodeImpl node = new NodeImpl(config.name().toString().toCharArray(), config.name(), TypeToken.TAG);
for (Class<?> clazz : children)
{
if (clazz.isInstance(node))
return true;
}
return false;
}
static boolean isEndType(HTMLNode node, NodeConfig config)
{
Class<?>[] classes = config.end_types();
for (Class<?> clazz : classes)
{
if (clazz != node.getConfig().type())
continue;
return true;
}
Name[] names = config.end_names();
for (Name name : names)
{
if (node.getConfig().name() != name)
continue;
return true;
}
return false;
}
}