/* * Copyright (c) 2015-2015 Vladimir Schneider <vladimir.schneider@gmail.com> * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * * This file is based on the IntelliJ Markdown Support plugin * */ package com.vladsch.idea.multimarkdown.parser; import com.vladsch.idea.multimarkdown.settings.MultiMarkdownGlobalSettings; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.parboiled.errors.ParserRuntimeException; import org.pegdown.ParsingTimeoutException; import org.pegdown.PegDownProcessor; import org.pegdown.ast.Node; import org.pegdown.ast.RootNode; import java.util.ArrayList; public class MultiMarkdownLexParserManager { private static final Logger logger = org.apache.log4j.Logger.getLogger(MultiMarkdownLexParserManager.class); private static final boolean log = false; private static final boolean disable = true; private static final ThreadLocal<ParsingInfo> lastParsingResult = new ThreadLocal<ParsingInfo>(); public static final int GITHUB_WIKI_LINKS = 0x80000000; public static RootNode parseMarkdownRoot(@NotNull final CharSequence buffer, @Nullable Integer pegdownExtensions, @Nullable Integer parsingTimeout) { int actualPegdownExtensions = (pegdownExtensions != null ? pegdownExtensions : MultiMarkdownGlobalSettings.getInstance().getExtensionsValue()) | (MultiMarkdownGlobalSettings.getInstance().githubWikiLinks.getValue() ? GITHUB_WIKI_LINKS : 0); if (!disable) { final ParsingInfo info = lastParsingResult.get(); if (info != null && info.pegdownExtensions == actualPegdownExtensions && info.bufferHash == buffer.hashCode() && info.buffer.equals(buffer)) { if (log) logger.info("Root Parsing request satisfied by cache for thread " + Thread.currentThread()); return info.rootNode; } if (log) logger.info("Root Parsing request not satisfied by cache for thread " + Thread.currentThread()); } PegDownProcessor processor = new PegDownProcessor(actualPegdownExtensions, parsingTimeout != null ? parsingTimeout : MultiMarkdownGlobalSettings.getInstance().parsingTimeout.getValue()); char[] currentChars = buffer.toString().toCharArray(); RootNode rootNode = processor.parseMarkdown(currentChars); String exceptionText = null; try { rootNode = processor.parseMarkdown(currentChars); } catch (ParsingTimeoutException e) { exceptionText = e.getMessage(); } catch (ParserRuntimeException e) { exceptionText = e.getMessage(); } if (rootNode == null) { ErrorNode errorNode = new ErrorNode("Parser Timeout Error: " + exceptionText); errorNode.setStartIndex(0); errorNode.setEndIndex(buffer.length()); ArrayList<Node> nodes = new ArrayList<Node>(); nodes.add(errorNode); rootNode = new RootNode(nodes); } if (!disable) lastParsingResult.set(new ParsingInfo(buffer, actualPegdownExtensions, rootNode, null, false)); return rootNode; } public static @Nullable MultiMarkdownLexParser.LexerToken[] parseMarkdown(@NotNull final CharSequence buffer, @Nullable Integer pegdownExtensions, @Nullable Integer parsingTimeout) { int actualPegdownExtensions = (pegdownExtensions != null ? pegdownExtensions : MultiMarkdownGlobalSettings.getInstance().getExtensionsValue()) | (MultiMarkdownGlobalSettings.getInstance().githubWikiLinks.getValue() ? GITHUB_WIKI_LINKS : 0); RootNode rootNode = null; if (!disable) { final ParsingInfo info = lastParsingResult.get(); if (info != null && info.pegdownExtensions == actualPegdownExtensions && info.bufferHash == buffer.hashCode() && info.buffer.equals(buffer)) { if (info.hadLexerTokens) { if (log) logger.info("LexerToken Parsing request satisfied by cache for thread " + Thread.currentThread()); return info.lexerTokens; } if (log) logger.info("LexerToken Parsing request partially satisfied by cache for thread " + Thread.currentThread()); rootNode = info.rootNode; } } char[] currentChars = buffer.toString().toCharArray(); if (rootNode == null) { if (log) logger.info("LexerToken Parsing request not satisfied by cache for thread " + Thread.currentThread()); PegDownProcessor processor = new PegDownProcessor(actualPegdownExtensions, parsingTimeout != null ? parsingTimeout : MultiMarkdownGlobalSettings.getInstance().parsingTimeout.getValue()); String exceptionText = null; try { rootNode = processor.parseMarkdown(currentChars); } catch (ParsingTimeoutException e) { exceptionText = e.getMessage(); } catch (ParserRuntimeException e) { exceptionText = e.getMessage(); } if (rootNode == null) { ErrorNode errorNode = new ErrorNode("Parser Timeout Error: " + exceptionText); errorNode.setStartIndex(0); errorNode.setEndIndex(buffer.length()); ArrayList<Node> nodes = new ArrayList<Node>(); nodes.add(errorNode); rootNode = new RootNode(nodes); } } MultiMarkdownLexParser lexParser = new MultiMarkdownLexParser(); MultiMarkdownLexParser.LexerToken[] lexerTokens = lexParser.parseMarkdown(rootNode, currentChars, actualPegdownExtensions); if (!disable) lastParsingResult.set(new ParsingInfo(buffer, actualPegdownExtensions, rootNode, lexerTokens, true)); return lexerTokens; } private static class ParsingInfo { @NotNull final CharSequence buffer; @Nullable final RootNode rootNode; @Nullable MultiMarkdownLexParser.LexerToken[] lexerTokens; final int bufferHash; final int pegdownExtensions; final boolean hadLexerTokens; public ParsingInfo(@NotNull CharSequence buffer, int pegdownExtensions, @Nullable RootNode rootNode, @Nullable MultiMarkdownLexParser.LexerToken[] lexerTokens, boolean hadLexerTokens) { this.buffer = buffer; this.bufferHash = buffer.hashCode(); this.rootNode = rootNode; this.pegdownExtensions = pegdownExtensions; this.lexerTokens = lexerTokens; this.hadLexerTokens = hadLexerTokens || rootNode == null || lexerTokens != null; } } }