de.micromata.genome.gwiki.page.impl.wiki.parser.GWikiWikiParser.java Source code

Java tutorial

Introduction

Here is the source code for de.micromata.genome.gwiki.page.impl.wiki.parser.GWikiWikiParser.java

Source

//
// Copyright (C) 2010-2016 Roger Rene Kommer & Micromata GmbH
//
// 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 de.micromata.genome.gwiki.page.impl.wiki.parser;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import de.micromata.genome.gwiki.page.GWikiContext;
import de.micromata.genome.gwiki.page.impl.GWikiContent;
import de.micromata.genome.gwiki.page.impl.wiki.GWikiCompileTimeMacro;
import de.micromata.genome.gwiki.page.impl.wiki.GWikiMacroFragment;
import de.micromata.genome.gwiki.page.impl.wiki.GWikiMacroRenderFlags;
import de.micromata.genome.gwiki.page.impl.wiki.GWikiRuntimeMacro;
import de.micromata.genome.gwiki.page.impl.wiki.MacroAttributes;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragment;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentBr;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentBrInLine;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentChildContainer;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentFixedFont;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentHeading;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentHr;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentImage;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentLi;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentLink;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentList;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentP;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentParseError;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentTable;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentText;
import de.micromata.genome.gwiki.page.impl.wiki.fragment.GWikiFragmentTextDeco;

/**
 * Parser implementation for the GWiki syntax.
 * 
 * @author Roger Rene Kommer (r.kommer@micromata.de)
 * 
 */
public class GWikiWikiParser {
    protected GWikiFragmentList getNestedLiChild(GWikiFragmentList pl) {
        if (pl.getChilds().isEmpty() == true) {
            return pl;
        }
        GWikiFragment lifr = pl.getChilds().get(pl.getChilds().size() - 1);
        if ((lifr instanceof GWikiFragmentLi) == false) {
            return pl;
        }
        GWikiFragmentLi lifrl = (GWikiFragmentLi) lifr;
        if (lifrl.getChilds().isEmpty() == true) {
            return pl;
        }
        GWikiFragment nlc = lifrl.getChilds().get(lifrl.getChilds().size() - 1);
        if (nlc instanceof GWikiFragmentList) {
            return getNestedLiChild((GWikiFragmentList) nlc);
        }
        return pl;
    }

    protected GWikiFragmentList findNestedListChild(GWikiFragmentList pl, String tag) {
        if (pl.getListTag().equals(tag) == true) {
            return pl;
        }
        if (pl.getChilds().isEmpty() == true) {
            return null;
        }
        GWikiFragment nlc = pl.getChilds().get(pl.getChilds().size() - 1);
        if ((nlc instanceof GWikiFragmentLi) == false) {
            return null;
        }
        GWikiFragmentLi nli = (GWikiFragmentLi) nlc;
        if (nli.getChilds().isEmpty() == true
                || (nli.getChilds().get(nli.getChilds().size() - 1) instanceof GWikiFragmentList) == false) {
            return null;
        }
        GWikiFragmentList rn = (GWikiFragmentList) nli.getChilds().get(nli.getChilds().size() - 1);
        return findNestedListChild(rn, tag);
    }

    protected GWikiFragmentList findBestNestedListChildN(GWikiFragmentList pl, String tag) {
        if (pl.getChilds().isEmpty() == true) {
            return null;
        }
        GWikiFragment lifr = pl.getChilds().get(pl.getChilds().size() - 1);
        if ((lifr instanceof GWikiFragmentLi) == false) {
            return null;
        }
        GWikiFragmentLi nli = (GWikiFragmentLi) lifr;
        if (nli.getChilds().isEmpty() == true) {
            return null;
        }
        if ((nli.getChilds().get(nli.getChilds().size() - 1) instanceof GWikiFragmentList) == false) {
            return null;
        }
        GWikiFragmentList nlc = (GWikiFragmentList) nli.getChilds().get(nli.getChilds().size() - 1);
        return findBestNestedListChild(nlc, tag);
    }

    protected GWikiFragmentList findBestNestedListChild(GWikiFragmentList pl, String tag) {
        GWikiFragmentList ret = findBestNestedListChildN(pl, tag);
        if (ret != null) {
            return ret;
        }
        if (tag.startsWith(pl.getListTag()) == true) {
            return pl;
        }
        return null;
    }

    private boolean isNestedOrThisLi(GWikiFragment lfrag, String tag) {
        if ((lfrag instanceof GWikiFragmentList) == false) {
            return false;
        }
        GWikiFragmentList llfrag = (GWikiFragmentList) lfrag;

        String prefix = llfrag.getListTag();
        if (tag.startsWith(prefix) == false) {
            return false;
        }
        return true;
    }

    protected void parseLi(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        //
        // * x -> * x
        // ** z -> * z
        //
        char tk = tks.curToken();
        String tag = tks.getStringUntilNotOneOf("-*#");

        GWikiFragment lfrag = ctx.lastFragment();
        GWikiFragmentList listfrag = new GWikiFragmentList(tag);
        try {
            ctx.pushFragStack(listfrag);
            if (isNestedOrThisLi(lfrag, tag) == true) {
                GWikiFragmentList pl = (GWikiFragmentList) lfrag;
                GWikiFragmentList prevlist = findNestedListChild(pl, tag);
                if (prevlist != null) {
                    listfrag = prevlist;
                } else {
                    prevlist = findBestNestedListChild(pl, tag);
                    if (prevlist != null) {
                        if (prevlist.getChilds().isEmpty() == false && prevlist.getChilds()
                                .get(prevlist.getChilds().size() - 1) instanceof GWikiFragmentLi) {
                            GWikiFragmentLi lc = (GWikiFragmentLi) prevlist.getChilds()
                                    .get(prevlist.getChilds().size() - 1);
                            lc.addChild(listfrag);
                            // prevlist.addChild(listfrag);
                        } else {
                            prevlist.addChild(listfrag);
                        }
                    } else {
                        pl.addChild(listfrag);
                    }
                }
            } else {
                ctx.addFragment(listfrag);
            }
            ctx.pushFragList();
            tk = tks.nextToken(); // *
            tk = tks.skipWs();
            tks.addStopToken('\n');
            parseLine(tks, ctx);
            List<GWikiFragment> childs = ctx.popFragList();
            if (childs.size() > 0 && childs.get(childs.size() - 1) instanceof GWikiFragmentBr) {
                childs = new ArrayList<>(childs.subList(0, childs.size() - 1));
            }
            if (childs.size() == 1 && childs.get(0) instanceof GWikiFragmentList) {
                listfrag.addChilds(childs);
            } else {
                listfrag.addChild(new GWikiFragmentLi(listfrag, childs));
            }
            tks.removeStopToken('\n');
        } finally {
            ctx.popFragStack();
        }
    }

    protected boolean isDecorateStart(GWikiWikiTokens tks) {
        char pk = tks.peekToken(-1);
        if (pk != 0 && Character.isLetterOrDigit(pk) == true) {
            return false;
        }
        pk = tks.peekToken(1);
        if (pk == 0) {
            return false;
        }
        if (Character.isWhitespace(pk) == true) {
            return false;
        }
        return true;

    }

    protected boolean isDecorateEnd(GWikiWikiTokens tks) {
        char pk = tks.peekToken(-1);
        if (pk == 0 || Character.isWhitespace(pk) == true) {
            return false;
        }
        pk = tks.peekToken(1);
        if (pk == 0) {
            return true;
        }
        if (Character.isLetterOrDigit(pk) == true) {
            return false;
        }
        return true;

    }

    protected String getWordDecorator(char decChar) {
        switch (decChar) {
        case '*':
            return "b";
        case '_':
            return "em";
        case '-':
            return "del";
        case '~':
            return "sub";
        case '^':
            return "sup";
        case '+':
            return "u";
        default:
            return "";
        }
    }

    protected void parseDecoratedWord(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char decChar = tks.curToken();
        char pk = tks.peekToken(-1);
        if (isDecorateStart(tks) == false) {
            ctx.addTextFragement(tks.curTokenString());
            if (tks.hasNext() == true) {
                tks.nextToken();
                parseWord(tks, ctx);
            } else {
                tks.nextToken();
            }
            return;
        }
        //
        int ltkpos = tks.getTokenPos();
        tks.nextToken();
        char prevDecChar = tks.addStopToken(decChar);
        List<GWikiFragment> nestedList = new ArrayList<GWikiFragment>();
        do {
            ctx.pushFragList();
            parseWords(tks, ctx);
            List<GWikiFragment> nested = ctx.popFragList();
            nestedList.addAll(nested);
            if (prevDecChar == 0) {
                tks.removeStopToken(decChar);
            }

            char ct = tks.curToken();
            if (ct == '\n' || ct == 0) {
                ctx.addTextFragement(Character.toString(decChar));
                tks.setTokenPos(ltkpos + 1);
                return;
            }
            // char ct = tks.nextToken();
            if (ct == decChar && isDecorateEnd(tks) == true) {
                String tag = getWordDecorator(decChar);
                GWikiFragmentTextDeco frag = new GWikiFragmentTextDeco(decChar, "<" + tag + ">", "</" + tag + ">",
                        nestedList);
                ctx.addFragment(frag);
                tks.nextToken();
                return;
            }
            if (tks.hasNext() == false) {
                ctx.addTextFragement(Character.toString(decChar));
                tks.setTokenPos(ltkpos + 1);
                return;
            }
        } while (true);
    }

    protected GWikiMacroFragment parseMacroHead(MacroAttributes ma, GWikiWikiTokens tks,
            GWikiWikiParserContext ctx) {

        // {macroName}
        // {macroName:defaultArg}
        // {macroName:arg=value} //
        // {macroName:arg=value|arg2=value2}
        GWikiMacroFragment mf = ctx.createMacro(ma);
        char tk = tks.curToken();
        if (tk == '}') {
            tks.nextToken();
            return mf;
        }
        if (tk != ':') {
            return null;// TODO error

        }
        tk = tks.nextToken();
        do {
            tk = tks.curToken();
            String label = tks.getStringUntilOneOf("|}=");
            tk = tks.curToken();
            if (tk == '}' || tk == '|') {
                ma.getArgs().setStringValue(MacroAttributes.DEFAULT_VALUE_KEY, label);
                tks.nextToken();
                if (tk == '}') {
                    return mf;
                }

            } else if (tk == '=') {
                tk = tks.nextToken();
                String val;
                if (tk == '"') {
                    tks.nextToken();
                    val = tks.parseStringQuoted();
                } else {
                    val = tks.getStringUntilOneOf("|}");
                }
                ma.getArgs().setStringValue(label, val);
            }
            tk = tks.curToken();
            if (tk == '}') {
                tks.nextToken();
                return mf;
            } else if (tk == '|') {
                tks.nextToken();
            } else {
                // TODO error
            }
        } while (tks.eof() == false);
        return null;
    }

    protected List<GWikiFragment> removeWsTokensFromEnd(List<GWikiFragment> childs) {
        for (int i = childs.size() - 1; i >= 0; --i) {
            GWikiFragment frag = childs.get(i);
            if (/* frag instanceof GWikiFragmentP || */frag instanceof GWikiFragmentBr) {
                childs.remove(i);
                continue;
            }
            if (frag instanceof GWikiFragmentText) {
                GWikiFragmentText t = (GWikiFragmentText) frag;
                String s = t.getSource();
                if (StringUtils.isBlank(s) == true) {
                    childs.remove(i);
                    continue;
                }
            }
            break;
        }
        return childs;
    }

    public static boolean isPAllowedInDom(GWikiWikiParserContext ctx) {
        for (GWikiFragment frag : ctx.getFragStack()) {
            if (isParagraphLike(frag) == false) {
                return false;
            }
        }
        return true;
    }

    protected void parseMacro(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        int curTokePos = tks.getTokenPos();
        char tk = tks.nextToken();
        String macroName = tks.curTokenString();
        MacroAttributes ma = new MacroAttributes(macroName);
        tk = tks.nextToken();
        // tks.nextToken();
        GWikiMacroFragment frag = parseMacroHead(ma, tks, ctx);
        if (frag == null) {
            ctx.addTextFragement("{");
            tks.setTokenPos(curTokePos + 1);
            return;
        }
        ctx.pushFragList();
        try {
            ctx.pushFragStack(frag);
            if (frag.getMacro().hasBody() == false) {
                ctx.addFragments(ctx.popFragList());
                if (frag.getMacro() instanceof GWikiCompileTimeMacro) {
                    Collection<GWikiFragment> nfrags = ((GWikiCompileTimeMacro) frag.getMacro()).getFragments(frag,
                            tks, ctx);
                    ctx.addFragments(nfrags);
                } else if (frag.getMacro() instanceof GWikiRuntimeMacro) {
                    ctx.addFragment(frag);
                } else {
                    ctx.addFragment(
                            new GWikiFragmentParseError("Macro is neither Compile nor Runtime Macro: " + macroName,
                                    tks.getLineNoFromTokenOffset(curTokePos)));
                }
                return;
            } else if (frag.getMacro() instanceof GWikiCompileTimeMacro) {
                // compile time
                Collection<GWikiFragment> nfrags = ((GWikiCompileTimeMacro) frag.getMacro()).getFragments(frag, tks,
                        ctx);
                ctx.addFragments(nfrags);
            }
            tk = tks.curToken();
            int tkn = tk;
            int startToken = tks.getTokenPos();
            if (GWikiMacroRenderFlags.TrimTextContent.isSet(frag.getMacro().getRenderModes()) == true) {
                tk = tks.skipWsNl();

            }
            if (frag.getMacro().evalBody() == true) {
                ctx.pushFragList();
                do {
                    tks.addStopToken('{');
                    parseMacroBody(tks, ctx);
                    tk = tks.curToken(true);
                    if (tk == '{') {
                        if (tks.peekToken(1, true) == '{') {
                            tks.removeStopToken('{');
                            parseWords(tks, ctx);
                            tks.addStopToken('{');
                            continue;
                        }
                        int tkss = tks.getTokenPos();
                        tks.nextToken();
                        String nmacroName = tks.curTokenString();
                        if (nmacroName.equals(macroName) == false) {
                            tks.setTokenPos(tkss);
                            tks.removeStopToken('{');
                            parseMacro(tks, ctx);
                            continue;
                        }
                        tks.nextToken();
                        MacroAttributes nma = new MacroAttributes(nmacroName);
                        GWikiMacroFragment nmf = parseMacroHead(nma, tks, ctx);
                        if (nmf.getAttrs().getArgs().isEmpty() == true) {
                            tks.removeStopToken('{');
                            break;
                        }
                        tks.setTokenPos(tkss);
                        tks.removeStopToken('{');
                        parseMacro(tks, ctx);
                        continue;
                    } else {
                        List<GWikiFragment> body = ctx.popFragList();
                        String source = frag.getSource();
                        ctx.addFragment(
                                new GWikiFragmentParseError("Missing macro end for  " + macroName + "; " + source,
                                        tks.getLineNoFromTokenOffset(curTokePos)));
                        ctx.addFragments(body);
                        ctx.addFragments(ctx.popFragList());
                        return;
                    }
                } while (true);

                List<GWikiFragment> childs = ctx.popFragList();
                if (GWikiMacroRenderFlags.TrimTextContent.isSet(frag.getMacro().getRenderModes()) == true) {
                    childs = removeWsTokensFromEnd(childs);
                }
                if (GWikiMacroRenderFlags.ContainsTextBlock.isSet(frag.getMacro().getRenderModes()) == true
                        && isPAllowedInDom(ctx)) {
                    // TODO frisst P
                    childs = addWrappedP(childs);
                }
                frag.addChilds(childs);
                ma.setChildFragment(new GWikiFragmentChildContainer(frag.getChilds()));
                if (GWikiMacroRenderFlags.TrimWsAfter.isSet(frag.getMacro().getRenderModes()) == true) {
                    tks.skipWsNl();
                }
            } else {
                int endToken = tks.findToken("{", frag.getAttrs().getCmd(), "}");
                if (endToken == -1) {
                    List<GWikiFragment> fragl = ctx.popFragList();
                    ctx.addFragment(new GWikiFragmentParseError("Missing macro end for  " + macroName,
                            tks.getLineNoFromTokenOffset(curTokePos)));
                    ctx.addFragments(fragl);
                    endToken = tks.getLastToken();
                    String body = tks.getTokenString(startToken, endToken);
                    ctx.addFragment(new GWikiFragmentText(body));
                    return;
                }
                String body = tks.getTokenString(startToken, endToken);
                if (GWikiMacroRenderFlags.TrimTextContent.isSet(frag.getMacro().getRenderModes()) == true) {
                    body = StringUtils.trim(body);
                }
                frag.getAttrs().setBody(body);
                tks.setTokenPos(endToken + 3);
            }
            ctx.addFragments(ctx.popFragList());
            if (frag.getMacro() instanceof GWikiCompileTimeMacro) {
                // ctx.addFragments(((GWikiCompileTimeMacro) frag.getMacro()).getFragments(frag, tks, ctx));
            } else if (frag.getMacro() instanceof GWikiRuntimeMacro) {
                ctx.addFragment(frag);
            } else {
                ctx.addFragment(
                        new GWikiFragmentParseError("Macro is neither Compile nor Runtime Macro: " + macroName,
                                tks.getLineNoFromTokenOffset(curTokePos)));
            }
        } finally {
            ctx.popFragStack();
        }
    }

    protected void parseFixedFontDecorator(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        tks.nextToken();
        int oldPos = tks.getTokenPos();
        List<GWikiFragment> nested = parseWordsUntil(tks, ctx, "}");

        if (tks.curToken(true) != '}' || tks.peekToken(1, true) != '}') {
            ctx.addTextFragement("{{");
            tks.setTokenPos(oldPos);
            return;
        }
        GWikiFragmentFixedFont frag = new GWikiFragmentFixedFont(nested);
        ctx.addFragment(frag);
        tks.nextToken();
        tks.nextToken();
    }

    /**
     * parse text until one of the given characters.
     * 
     * curToken is either one of given character or eof.
     * 
     * @param oneOf
     * @return
     */
    public List<GWikiFragment> parseWordsUntil(GWikiWikiTokens tks, GWikiWikiParserContext ctx, String oneOf) {
        return parseWordsUntil(tks, ctx, oneOf.toCharArray());
    }

    public List<GWikiFragment> parseWordsUntil(GWikiWikiTokens tks, GWikiWikiParserContext ctx, char[] oneOf) {
        char[] prevStopWords = new char[oneOf.length];
        for (int i = 0; i < oneOf.length; ++i) {
            prevStopWords[i] = tks.addStopToken(oneOf[i]);
        }
        ctx.pushFragList();
        parseWords(tks, ctx);
        List<GWikiFragment> ret = ctx.popFragList();
        for (int i = 0; i < oneOf.length; ++i) {
            if (prevStopWords[i] == 0) {
                tks.removeStopToken(oneOf[i]);
            }
        }
        return ret;
    }

    protected void parseLink(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        int oldPos = tks.getTokenPos();
        tks.nextToken();
        char barStop = tks.removeStopToken('|');
        String s = tks.getStringUntilOneOf("]");
        if (StringUtils.isEmpty(s) == true) {
            tks.setTokenPos(oldPos);
            tks.addStopToken(barStop);
            ctx.addTextFragement(tks.curTokenString());
            tks.nextToken();
            return;
        }
        GWikiFragmentLink link = new GWikiFragmentLink(s);
        if (link.isTitleDefined() == true) {
            link.addChilds(parseSubLine(ctx, link.getTitle()));
        }
        ctx.addFragment(link);
        tks.nextToken();

        tks.addStopToken(barStop);
    }

    protected void parseImage(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        int oldPos = tks.getTokenPos();
        char tk = tks.nextToken();
        // tks.addStopToken('!');
        String link = tks.getStringUntilOneOf("!\n", true);
        tk = tks.curToken();
        if (link == null || tk != '!') {
            ctx.addTextFragement("!");
            tks.setTokenPos(oldPos);
            // ctx.addTextFragement(tks.curTokenString());
            tks.nextToken();
            return;
        }
        tk = tks.nextToken();
        GWikiFragmentImage img = new GWikiFragmentImage(link);
        ctx.addFragment(img);
    }

    protected void parseWord(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char tk = tks.curToken();
        switch (tk) {
        case 0:
            return;
        case '+':
        case '^':
        case '~':
        case '*':
        case '_':
        case '-': {
            parseDecoratedWord(tks, ctx);
            break;
        }
        case '\\': {
            char nt = tks.nextTokenIgnoreStops();
            if (nt == '\\' && tks.peekToken(1, true) == '\n') {
                ctx.addFragment(new GWikiFragmentBrInLine());
                tk = tks.nextTokenIgnoreStops();
                tk = tks.nextTokenIgnoreStops();
            } else {
                ctx.addTextFragement(tks.curTokenString(true));
                tk = tks.nextToken();
            }

            break;
        }
        case '[':
            parseLink(tks, ctx);
            break;
        case '!':
            parseImage(tks, ctx);
            break;
        case '{':
            if (tks.peekToken(1) == '{') {
                tk = tks.nextToken();
                parseFixedFontDecorator(tks, ctx);
            } else {
                parseMacro(tks, ctx);
            }
            break;
        default:
            ctx.addTextFragement(tks.curTokenString());
            tk = tks.nextToken();
            break;
        }
    }

    protected void parseWords(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char tk = tks.curToken();
        if (tk == '\n') {
            return;
        }
        do {
            boolean inFixedFont = false;
            if (tk == 0) {
                if (tks.curToken(true) == '{' && tks.peekToken(1, true) == '{') {
                    inFixedFont = true;
                    tks.removeStopToken('{');
                } else {
                    break;
                }
            }

            parseWord(tks, ctx);
            if (inFixedFont == true) {
                tks.addStopToken('{');
            }
            tk = tks.curToken();
            if (tk == '\n') {
                break;
            }
        } while (true/* tk != 0 */);
    }

    protected boolean isSentenceTerminator(GWikiWikiParserContext ctx, char c) {
        if (".;!?:!".indexOf(c) == -1) {
            return false;
        }
        if (ctx.getFragStack().size() == 0) {
            return false;
        }
        GWikiFragment lfrag = ctx.getFragStack().peek(0);
        if (lfrag instanceof GWikiFragmentText) {
            return true;
        }
        return false;
    }

    protected void parseLineText(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        do {
            int prePos = tks.getTokenPos();
            parseWords(tks, ctx);
            int epos = tks.getTokenPos();
            if (tks.eof() == true || tks.hasNext() == false || tks.peekToken(0) == '\n') {
                break;
            }
            if (prePos == epos) {
                break;
            }
        } while (true);
        char preToken = tks.peekToken(-1);
        char curToken = tks.curToken();
        char nextToken = tks.peekToken(1);

        if (nextToken == '\n') {
            nextToken = tks.nextToken();

            ctx.addFragment(new GWikiFragmentP());
        } else if (isSentenceTerminator(ctx, preToken)) {
            ctx.addFragment(new GWikiFragmentBr());
        } else if (curToken == '\n') {
            if (tks.peekToken(-1) != '\\') {
                ctx.addFragment(new GWikiFragmentBr());
            }
        }

    }

    protected void parseLineHeadingText(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char tk = tks.curToken();
        String l = tks.curTokenString();
        if (l.length() == 2 && l.charAt(0) == 'h' && Character.isDigit(l.charAt(1)) == true) {
            if (tks.peekToken(1) == '.') {
                GWikiFragmentHeading hf = new GWikiFragmentHeading(Integer.valueOf(l.substring(1, 2)), "");
                try {
                    ctx.pushFragStack(hf);
                    tk = tks.nextToken();
                    tk = tks.nextToken();
                    tk = tks.skipWs();
                    ctx.pushFragList();
                    parseWords(tks, ctx);
                    hf.addChilds(ctx.popFragList());
                    ctx.addFragment(hf);
                    // ctx.addTextFragement("\n");
                    tk = tks.skipWsNl(false);
                    if (tk != -1) {
                        tks.pushBack();
                    }
                } finally {
                    ctx.popFragStack();
                }
                return;
            }
        }
        parseLineText(tks, ctx);
    }

    public GWikiFragmentTable.Cell parseTableCell(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        String style = "td";
        if (tks.peekToken(1, true) == '|') {
            tks.nextToken();
            style = "th";
        }
        char tk = tks.nextToken();
        if (tk == '\n' || tk == 0) {
            if (tk == '\n') {
                tks.nextToken();
            }
            return null;
        }

        ctx.pushFragList();
        tks.addStopToken('|');
        tks.addStopToken('\n');
        parseLiLine(tks, ctx);
        tks.removeStopToken('|');
        tks.removeStopToken('\n');
        GWikiFragmentChildContainer ct = new GWikiFragmentChildContainer(ctx.popFragList());
        return new GWikiFragmentTable.Cell(style, ct);
        // return new Pair<String, GWikiFragmentChildContainer>(style, ct);
    }

    protected void removeLastEmptyCell(GWikiFragmentTable.Row row) {
        if (row.cells.isEmpty() == true) {
            return;
        }
        GWikiFragmentTable.Cell cell = row.cells.get(row.cells.size() - 1);
        if (cell.attributes.getChildFragment() == null) {
            return;
        }
        if (cell.attributes.getChildFragment().getChilds().size() != 1) {
            return;
        }
        GWikiFragment frag = cell.attributes.getChildFragment().getChilds().get(0);
        if ((frag instanceof GWikiFragmentText) == false) {
            return;
        }
        GWikiFragmentText tfrag = (GWikiFragmentText) frag;
        if (StringUtils.isBlank(tfrag.getSource()) == false) {
            return;
        }
        row.cells.remove(row.cells.size() - 1);
    }

    public GWikiFragmentTable.Row parseTableLine(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        GWikiFragmentTable.Row row = new GWikiFragmentTable.Row();

        do {
            GWikiFragmentTable.Cell cell = parseTableCell(tks, ctx);
            if (cell == null) {
                return row;
            }
            row.addCell(cell);
            char tk = tks.curToken();
            if (tk == '\n') {
                removeLastEmptyCell(row);
                tks.nextToken();
                return row;
            }
        } while (tks.eof() == false);
        return null;
    }

    public void parseTable(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char tk;
        int startToken = tks.getTokenPos();
        GWikiFragmentTable table = new GWikiFragmentTable();
        try {
            ctx.pushFragStack(table);
            // List<List<Pair<String, GWikiFragmentChildContainer>>> tbl = new ArrayList<List<Pair<String, GWikiFragmentChildContainer>>>();
            do {
                GWikiFragmentTable.Row tb = parseTableLine(tks, ctx);
                if (tb == null) {
                    if (table.getRowSize() == 0) {
                        tks.setTokenPos(startToken);
                        tk = tks.curToken();
                        ctx.addTextFragement(tks.curTokenString());
                        tk = tks.nextToken();
                        if (tk == '|') {
                            ctx.addTextFragement(tks.curTokenString());
                            tk = tks.nextToken();
                        }
                        parseLine(tks, ctx);
                        return;
                    }
                    break;
                }
                table.addRow(tb);
                tk = tks.curToken();
                if (tk != '|') {
                    tks.pushBack();
                    break;
                }
            } while (tks.eof() == false);
            // table.setTable(tbl);
            ctx.addFragment(table);
        } finally {
            ctx.popFragStack();
        }
    }

    public void parseLiLine(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char ct = tks.curToken();
        switch (ct) {
        case '#':
        case '*':
        case '-': {
            char lookahead = tks.peekToken(1);
            int oldPos = tks.getTokenPos();
            String l = tks.getStringUntilOneOf("\n", true);
            if ("----".equals(l) == true) {
                ctx.addFragment(new GWikiFragmentHr());
                // ct = tks.nextToken();
                return;
            } else {
                tks.setTokenPos(oldPos);
            }
            if (Character.isWhitespace(lookahead) == true || "#*-".indexOf(lookahead) != -1) {
                parseLi(tks, ctx);
            } else {
                parseLineHeadingText(tks, ctx);
            }
            return;
        }
        default:
            parseLineHeadingText(tks, ctx);
            break;

        }
    }

    public void parseLine(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        char ct = tks.curToken();
        switch (ct) {
        case '\n': {
            char nt = tks.peekToken(1);
            if (nt == '\n' || nt == -1) {
                tks.nextToken();
                ctx.addFragment(new GWikiFragmentP());
            } else {
                ctx.addFragment(new GWikiFragmentBr());
            }

            break;
        }
        case '|':
            parseTable(tks, ctx);
            break;
        default:
            parseLiLine(tks, ctx);
            break;
        }
    }

    public List<GWikiFragment> parseSubParagraph(GWikiWikiParserContext otherParseContext, String text) {
        GWikiWikiParserContext parseContext = otherParseContext.createChildParseContext();
        parseFrags(text, parseContext);
        return parseContext.popFragList();
    }

    public List<GWikiFragment> parseSubLine(GWikiWikiParserContext otherParseContext, String text) {
        GWikiWikiParserContext ctx = otherParseContext.createChildParseContext();
        ctx.pushFragList();
        GWikiWikiTokens tks = createGWikiTokens(text);
        tks.nextToken();
        parseLiLine(tks, ctx);
        return ctx.popFragList();
    }

    public GWikiContent parse(GWikiContext wikiContext, String text) {
        GWikiWikiParserContext parseContext = new GWikiWikiParserContext();
        parseContext.getMacroFactories()
                .putAll(wikiContext.getWikiWeb().getWikiConfig().getWikiMacros(wikiContext));
        String ntext = StringUtils.replace(text, "\n\r", "\n");
        ntext = StringUtils.replace(ntext, "\r\n", "\n");
        // ntext = "\n" + ntext;
        parseFrags(ntext, parseContext);
        return new GWikiContent(parseContext.popFragList());

    }

    /**
     * Parse body. Different to parseText no nextToken will be called initialilly
     * 
     * @param tks
     * @param ctx
     */
    public void parseMacroBody(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        while (tks.eof() == false) {
            parseLine(tks, ctx);
            if (tks.eof() == true) {
                break;
            }
            tks.nextToken();
        }
    }

    public static boolean isParagraphLike(GWikiFragment ff) {
        return ff instanceof GWikiFragmentP || ff instanceof GWikiFragmentHeading
                || ff instanceof GWikiFragmentTable || ff instanceof GWikiFragmentHr
                || ff instanceof GWikiFragmentList || ff instanceof GWikiFragmentLi
                || (ff instanceof GWikiMacroFragment && GWikiMacroRenderFlags.NoWrapWithP
                        .isSet(((GWikiMacroFragment) ff).getMacro().getRenderModes()));

    }

    protected List<GWikiFragment> removeBrsAfterParagraph(List<GWikiFragment> l) {
        if (l.size() <= 1) {
            return l;
        }
        GWikiFragment ff = l.get(0);
        if (isParagraphLike(ff) == false) {
            return l;
        }
        if (l.size() == 3) {
            if (l.get(2) instanceof GWikiFragmentBr) {
                l = l.subList(0, 2);
            } else {
                return l;
            }
        }
        if (l.get(1) instanceof GWikiFragmentBr) {
            l = l.subList(0, 1);
        }
        return l;
    }

    public static List<GWikiFragment> trimBrs(List<GWikiFragment> l) {
        if (l.isEmpty() == true) {
            return l;
        }
        if (l.get(0) instanceof GWikiFragmentBr) {
            l = l.subList(1, l.size());
        }
        if (l.isEmpty() == true) {
            return l;
        }
        if (l.get(l.size() - 1) instanceof GWikiFragmentBr) {
            l = l.subList(0, l.size() - 1);
        }
        return l;
    }

    protected List<GWikiFragment> trimEndP(List<GWikiFragment> l) {
        if (l.isEmpty() == true) {
            return l;
        }
        if (l.get(l.size() - 1) instanceof GWikiFragmentP) {
            l = l.subList(0, l.size() - 1);
        }
        return l;
    }

    public static List<GWikiFragment> addWrappedP(List<GWikiFragment> l) {
        List<GWikiFragment> ret = new ArrayList<GWikiFragment>();
        int ls = 0;
        for (int i = 0; i < l.size(); ++i) {
            GWikiFragment f = l.get(i);
            if (isParagraphLike(f) == true) {
                if (i > ls) {
                    List<GWikiFragment> lp = l.subList(ls, i);
                    lp = trimBrs(lp);
                    // lp = trimEndP(lp);
                    ret.add(new GWikiFragmentP(new ArrayList<>(lp)));
                }
                if ((f instanceof GWikiFragmentP) == false) {
                    ret.add(f);
                }
                ls = i + 1;
            }
        }
        if (ls < l.size()) {
            List<GWikiFragment> lp = l.subList(ls, l.size());
            lp = trimBrs(lp);
            if (lp.isEmpty() == false) {
                ret.add(new GWikiFragmentP(new ArrayList<>(lp)));
            }
        }
        return ret;
    }

    public void parseText(GWikiWikiTokens tks, GWikiWikiParserContext ctx) {
        // so geht das nicht, da li nicht mehr geht
        // List<GWikiFragment> plist = new ArrayList<GWikiFragment>();
        int startPlIdx = -1;
        while (tks.hasNext() == true) {
            tks.nextToken();
            ctx.pushFragList();
            parseLine(tks, ctx);
            List<GWikiFragment> l = ctx.popFragList();
            if (l.isEmpty() == false) {
                ctx.addFragments(l);
            }
        }
        for (GWikiWikiParsePostprocessor proc : GWikiWikiParsePostprocessorRegistry.get().getProcessors()) {
            proc.process(ctx);
        }
    }

    public GWikiWikiTokens createGWikiTokens(String text) {
        String delimiter = "\n \t \\-*_|{}=#+^~[]!.:?;,\"";
        GWikiWikiTokens tks = new GWikiWikiTokens(delimiter, text);
        return tks;
    }

    public void parseFrags(String text, GWikiWikiParserContext ctx) {
        ctx.pushFragList();
        GWikiWikiTokens tks = createGWikiTokens(text);
        parseText(tks, ctx);
    }
}