All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sweble.wikitext.engine.ExpansionFrame Maven / Gradle / Ivy

Go to download

A minimal engine using the Sweble Wikitext Parser to process articles in the context of a MediaWiki-like configuration.

There is a newer version: 3.1.9
Show newest version
/**
 * Copyright 2011 The Open Source Research Group,
 *                University of Erlangen-Nürnberg
 *
 * 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 org.sweble.wikitext.engine;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.sweble.wikitext.engine.config.Namespace;
import org.sweble.wikitext.engine.config.WikiConfigurationInterface;
import org.sweble.wikitext.engine.log.IllegalNameException;
import org.sweble.wikitext.engine.log.ParseException;
import org.sweble.wikitext.engine.log.ResolveRedirectLog;
import org.sweble.wikitext.engine.log.ResolveTransclusionLog;
import org.sweble.wikitext.engine.log.UnhandledException;
import org.sweble.wikitext.lazy.AstNodeTypes;
import org.sweble.wikitext.lazy.LinkTargetException;
import org.sweble.wikitext.lazy.preprocessor.Redirect;
import org.sweble.wikitext.lazy.preprocessor.TagExtension;
import org.sweble.wikitext.lazy.preprocessor.Template;
import org.sweble.wikitext.lazy.preprocessor.TemplateArgument;
import org.sweble.wikitext.lazy.preprocessor.TemplateParameter;
import org.sweble.wikitext.lazy.utils.StringConversionException;
import org.sweble.wikitext.lazy.utils.StringConverter;
import org.sweble.wikitext.lazy.utils.XmlAttribute;

import de.fau.cs.osr.ptk.common.EntityMap;
import de.fau.cs.osr.ptk.common.Warning;
import de.fau.cs.osr.ptk.common.ast.AstNode;
import de.fau.cs.osr.ptk.common.ast.ContentNode;
import de.fau.cs.osr.ptk.common.ast.NodeList;
import de.fau.cs.osr.ptk.common.ast.Text;
import de.fau.cs.osr.utils.StopWatch;

public class ExpansionFrame
{
	private final Compiler compiler;
	
	private final ExpansionFrame rootFrame;
	
	private final ExpansionFrame parentFrame;
	
	private final PageTitle title;
	
	private final Map arguments;
	
	private final boolean forInclusion;
	
	private final ContentNode frameLog;
	
	private final ExpansionCallback callback;
	
	private final List warnings;
	
	private final EntityMap entityMap;
	
	// =========================================================================
	
	public ExpansionFrame(
	        Compiler compiler,
	        ExpansionCallback callback,
	        PageTitle title,
	        EntityMap entityMap,
	        List warnings,
	        ContentNode frameLog)
	{
		this.compiler = compiler;
		this.callback = callback;
		this.title = title;
		this.entityMap = entityMap;
		this.arguments = new HashMap();
		this.forInclusion = false;
		this.warnings = warnings;
		this.frameLog = frameLog;
		this.rootFrame = this;
		this.parentFrame = null;
	}
	
	public ExpansionFrame(
	        Compiler compiler,
	        ExpansionCallback callback,
	        PageTitle title,
	        EntityMap entityMap,
	        Map arguments,
	        boolean forInclusion,
	        ExpansionFrame rootFrame,
	        ExpansionFrame parentFrame,
	        List warnings,
	        ContentNode frameLog)
	{
		this.compiler = compiler;
		this.callback = callback;
		this.title = title;
		this.entityMap = entityMap;
		this.arguments = arguments;
		this.forInclusion = forInclusion;
		this.warnings = warnings;
		this.frameLog = frameLog;
		this.rootFrame = rootFrame;
		this.parentFrame = parentFrame;
	}
	
	// =========================================================================
	
	public Compiler getCompiler()
	{
		return compiler;
	}
	
	public ExpansionFrame getRootFrame()
	{
		return rootFrame;
	}
	
	public ExpansionFrame getParentFrame()
	{
		return parentFrame;
	}
	
	public PageTitle getTitle()
	{
		return title;
	}
	
	public Map getArguments()
	{
		return arguments;
	}
	
	public boolean isForInclusion()
	{
		return forInclusion;
	}
	
	public ContentNode getFrameLog()
	{
		return frameLog;
	}
	
	public WikiConfigurationInterface getWikiConfig()
	{
		return compiler.getWikiConfig();
	}
	
	public void fileWarning(Warning warning)
	{
		warnings.add(warning);
	}
	
	private void addAllWarnings(Collection warnings)
	{
		this.warnings.addAll(warnings);
	}
	
	public List getWarnings()
	{
		return warnings;
	}
	
	// =========================================================================
	
	public AstNode expand(AstNode ppAst)
	{
		return (AstNode) new ExpansionVisitor(this).go(ppAst);
	}
	
	// =========================================================================
	
	protected AstNode resolveRedirect(Redirect n, String target)
	{
		ResolveRedirectLog log = new ResolveRedirectLog(target, false);
		frameLog.getContent().add(log);
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		try
		{
			// FIXME: What if we don't want to redirect, but view the redirect 
			//        page itself? Then we probably don't want to return the
			//        contents...
			return redirectTo(n, target, log);
		}
		catch (LinkTargetException e)
		{
			// FIXME: Should we rather file a warning? Or additionally file a warning?
			log.getContent().add(new ParseException(e.getMessage()));
			
			return n;
		}
		catch (Throwable e)
		{
			StringWriter w = new StringWriter();
			e.printStackTrace(new PrintWriter(w));
			log.getContent().add(new UnhandledException(e, w.toString()));
			
			return n;
		}
		finally
		{
			stopWatch.stop();
			log.setTimeNeeded(stopWatch.getElapsedTime());
		}
	}
	
	protected AstNode resolveParserFunction(
	        Template n,
	        String target,
	        List arguments)
	{
		ResolveTransclusionLog log = new ResolveTransclusionLog(target, false);
		frameLog.getContent().add(log);
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		try
		{
			ParserFunctionBase pfn = getWikiConfig().getParserFunction(target);
			if (pfn == null)
			{
				// FIXME: File warning that we couldn't find the parser function?
				return null;
			}
			
			return invokeParserFunction(n, pfn, arguments, log);
		}
		catch (Throwable e)
		{
			StringWriter w = new StringWriter();
			e.printStackTrace(new PrintWriter(w));
			log.getContent().add(new UnhandledException(e, w.toString()));
			
			return n;
		}
		finally
		{
			stopWatch.stop();
			log.setTimeNeeded(stopWatch.getElapsedTime());
		}
	}
	
	protected AstNode resolveTransclusionOrMagicWord(
	        Template n,
	        String target,
	        List arguments)
	{
		ResolveTransclusionLog log = new ResolveTransclusionLog(target, false);
		frameLog.getContent().add(log);
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		try
		{
			if (arguments.isEmpty())
			{
				ParserFunctionBase pfn = getWikiConfig().getParserFunction(target);
				if (pfn != null)
					return invokeParserFunction(n, pfn, arguments, log);
			}
			
			return transcludePage(n, target, arguments, log);
		}
		catch (LinkTargetException e)
		{
			// FIXME: Should we rather file a warning? Or additionally file a warning?
			log.getContent().add(new ParseException(e.getMessage()));
			
			return n;
		}
		catch (Throwable e)
		{
			StringWriter w = new StringWriter();
			e.printStackTrace(new PrintWriter(w));
			log.getContent().add(new UnhandledException(e, w.toString()));
			
			return n;
		}
		finally
		{
			stopWatch.stop();
			log.setTimeNeeded(stopWatch.getElapsedTime());
		}
	}
	
	protected AstNode resolveParameter(TemplateParameter n, String name)
	{
		return arguments.get(name);
	}
	
	protected AstNode resolveTagExtension(
	        TagExtension n,
	        String name,
	        NodeList attributes,
	        String body)
	{
		ResolveTransclusionLog log = new ResolveTransclusionLog(name, false);
		frameLog.getContent().add(log);
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		try
		{
			TagExtensionBase te = getWikiConfig().getTagExtension(name);
			if (te == null)
			{
				// FIXME: File warning that we couldn't find the tag extension?
				return n;
			}
			
			return invokeTagExtension(n, attributes, body, te);
		}
		catch (Throwable e)
		{
			StringWriter w = new StringWriter();
			e.printStackTrace(new PrintWriter(w));
			log.getContent().add(new UnhandledException(e, w.toString()));
			
			return n;
		}
		finally
		{
			stopWatch.stop();
			log.setTimeNeeded(stopWatch.getElapsedTime());
		}
	}
	
	private AstNode invokeTagExtension(
	        TagExtension tagExtension,
	        NodeList attributes,
	        String body,
	        TagExtensionBase te)
	{
		HashMap attrs = new HashMap();
		
		for (AstNode attr : attributes)
		{
			if (attr.isNodeType(AstNodeTypes.NT_XML_ATTRIBUTE))
			{
				XmlAttribute a = (XmlAttribute) attr;
				attrs.put(a.getName(), a.getValue());
			}
		}
		
		return te.invoke(this, tagExtension, attrs, body);
	}
	
	protected void illegalTemplateName(StringConversionException e, NodeList name)
	{
		frameLog.getContent().add(new IllegalNameException(
		        name,
		        "Cannot resolve target of transclusion to a simple name."));
	}
	
	protected void illegalParameterName(StringConversionException e, NodeList name)
	{
		frameLog.getContent().add(new IllegalNameException(
		        name,
		        "Cannot resolve name of parameter to a simple name."));
	}
	
	// =========================================================================
	
	/* FIXME: These don't work! You would have to clone a cached page first, 
	 * before you can perform any kind of template expansion!
	 * 
	private AstNode redirectTo(
	        Redirect n,
	        String target,
	        ResolveRedirectLog log)
	            throws ArticleTitleException, CompilerException, UnsupportedEncodingException
	{
		PageTitle title = new PageTitle(getWikiConfig(), target);
		
		log.setCanonical(title.getFullTitle());
		
		FullPreprocessedPage fpPage =
		        callback.retrievePreprocessedPage(this, title, forInclusion);
		
		if (fpPage == null)
		{
			FullPage page = getWikitext(title, log);
			if (page == null)
			{
				return n;
			}
			else
			{
				CompiledPage ppPage = this.compiler.preprocess(
				        page.getId(),
				        page.getText(),
				        forInclusion,
				        null);
				
				fpPage = new FullPreprocessedPage(
				        page.getId(),
				        forInclusion,
				        ppPage);
				
				callback.cachePreprocessedPage(this, fpPage);
				
				LazyPreprocessedPage lppPage =
				        (LazyPreprocessedPage) ppPage.getPage();
				
				CompiledPage compiledPage = this.compiler.expand(
				        callback,
				        page.getId(),
				        lppPage,
				        forInclusion,
				        arguments,
				        rootFrame,
				        this);
				
				log.setSuccess(true);
				log.getContent().append(compiledPage.getLog());
				
				return ((LazyPreprocessedPage) compiledPage.getPage()).getContent();
			}
		}
		else
		{
			LazyPreprocessedPage lppPage =
			        (LazyPreprocessedPage) fpPage.getPage().getPage();
			
			CompiledPage compiledPage = this.compiler.expand(
			        callback,
			        fpPage.getId(),
			        lppPage,
			        forInclusion,
			        arguments,
			        rootFrame,
			        this);
			
			log.setSuccess(true);
			log.getContent().append(compiledPage.getLog());
			
			return ((LazyPreprocessedPage) compiledPage.getPage()).getContent();
		}
	}
	
	private AstNode transcludePage(
	        Template n,
	        String target,
	        List arguments,
	        ResolveTransclusionLog log)
	            throws ArticleTitleException, CompilerException, UnsupportedEncodingException
	{
		Namespace tmplNs = getWikiConfig().getTemplateNamespace();
		
		PageTitle title = new PageTitle(getWikiConfig(), target, tmplNs);
		
		log.setCanonical(title.getFullTitle());
		
		FullPreprocessedPage fpPage =
		        callback.retrievePreprocessedPage(this, title, true);
		
		if (fpPage == null)
		{
			FullPage page = getWikitext(title, log);
			if (page == null)
			{
				return null;
			}
			else
			{
				Map args =
				        prepareTransclusionArguments(arguments);
				
				CompiledPage ppPage = this.compiler.preprocess(
				        page.getId(),
				        page.getText(),
				        true,
				        null);
				
				fpPage = new FullPreprocessedPage(
				        page.getId(),
				        true,
				        ppPage);
				
				callback.cachePreprocessedPage(this, fpPage);
				
				LazyPreprocessedPage lppPage =
				        (LazyPreprocessedPage) ppPage.getPage();
				
				CompiledPage compiledPage = this.compiler.expand(
				        callback,
				        page.getId(),
				        lppPage,
				        true,
				        args,
				        rootFrame,
				        this);
				
				log.setSuccess(true);
				log.getContent().append(compiledPage.getLog());
				
				return ((LazyPreprocessedPage) compiledPage.getPage()).getContent();
			}
		}
		else
		{
			Map args =
			        prepareTransclusionArguments(arguments);
			
			LazyPreprocessedPage lppPage =
			        (LazyPreprocessedPage) fpPage.getPage().getPage();
			
			CompiledPage compiledPage = this.compiler.expand(
			        callback,
			        fpPage.getId(),
			        lppPage,
			        true,
			        args,
			        rootFrame,
			        this);
			
			log.setSuccess(true);
			log.getContent().append(compiledPage.getLog());
			
			return ((LazyPreprocessedPage) compiledPage.getPage()).getContent();
		}
	}
	*/

	// =====================================================================
	
	private AstNode redirectTo(
	        Redirect n,
	        String target,
	        ResolveRedirectLog log) throws LinkTargetException, CompilerException
	{
		PageTitle title = PageTitle.make(getWikiConfig(), target);
		
		log.setCanonical(title.getFullTitle());
		
		FullPage page = getWikitext(title, log);
		if (page == null)
		{
			// FIXME: File warning that we couldn't find the page?
			return n;
		}
		else
		{
			CompiledPage compiledPage = this.compiler.preprocessAndExpand(
			        callback,
			        page.getId(),
			        page.getText(),
			        forInclusion,
			        entityMap,
			        arguments,
			        rootFrame,
			        this);
			
			log.setSuccess(true);
			log.getContent().add(compiledPage.getLog());
			
			addAllWarnings(compiledPage.getWarnings());
			
			return compiledPage.getPage().getContent();
		}
	}
	
	private AstNode transcludePage(
	        Template n,
	        String target,
	        List arguments,
	        ResolveTransclusionLog log) throws LinkTargetException, CompilerException
	{
		Namespace tmplNs = getWikiConfig().getTemplateNamespace();
		
		PageTitle title = PageTitle.make(getWikiConfig(), target, tmplNs);
		
		log.setCanonical(title.getFullTitle());
		
		FullPage page = getWikitext(title, log);
		if (page == null)
		{
			// FIXME: File warning that we couldn't find the page?
			return null;
		}
		else
		{
			Map args =
			        prepareTransclusionArguments(arguments);
			
			CompiledPage compiledPage = this.compiler.preprocessAndExpand(
			        callback,
			        page.getId(),
			        page.getText(),
			        true,
			        entityMap,
			        args,
			        rootFrame,
			        this);
			
			log.setSuccess(true);
			log.getContent().add(compiledPage.getLog());
			
			addAllWarnings(compiledPage.getWarnings());
			
			return compiledPage.getPage().getContent();
		}
	}
	
	private Map prepareTransclusionArguments(List arguments)
	{
		HashMap args = new HashMap();
		
		for (TemplateArgument arg : arguments)
		{
			String id = String.valueOf(args.size() + 1);
			
			args.put(id, arg.getValue());
			
			if (arg.getHasName())
			{
				try
				{
					String name = StringConverter.convert(arg.getName());
					
					name = name.trim();
					
					if (!name.isEmpty() && !name.equals(id))
						args.put(name, arg.getValue());
				}
				catch (StringConversionException e)
				{
					illegalParameterName(e, arg.getName());
				}
			}
		}
		
		return args;
	}
	
	private AstNode invokeParserFunction(Template template, ParserFunctionBase pfn, List arguments, ResolveTransclusionLog log)
	{
		LinkedList args = new LinkedList();
		
		for (TemplateArgument arg : arguments)
		{
			if (arg.getHasName())
			{
				args.add(new NodeList(
				        arg.getName(),
				        new Text("="),
				        arg.getValue()));
			}
			else
			{
				args.add(arg.getValue());
			}
		}
		
		AstNode result = pfn.invoke(template, this, args);
		
		log.setSuccess(true);
		
		return result;
	}
	
	private FullPage getWikitext(PageTitle title, ContentNode log)
	{
		try
		{
			return callback.retrieveWikitext(this, title);
		}
		catch (Exception e)
		{
			StringWriter w = new StringWriter();
			e.printStackTrace(new PrintWriter(w));
			log.getContent().add(new UnhandledException(e, w.toString()));
			return null;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy