<commit_before>package org.strategoxt.imp.runtime.services;

import static org.spoofax.interpreter.core.Tools.asJavaString;
import static org.spoofax.interpreter.core.Tools.isTermAppl;
import static org.spoofax.interpreter.core.Tools.isTermString;
import static org.spoofax.interpreter.core.Tools.isTermTuple;
import static org.spoofax.interpreter.core.Tools.termAt;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.spoofax.interpreter.core.InterpreterErrorExit;
import org.spoofax.interpreter.core.InterpreterException;
import org.spoofax.interpreter.core.InterpreterExit;
import org.spoofax.interpreter.core.UndefinedStrategyException;
import org.spoofax.interpreter.terms.IStrategoString;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.strategoxt.imp.runtime.EditorState;
import org.strategoxt.imp.runtime.Environment;
import org.strategoxt.imp.runtime.RuntimeActivator;
import org.strategoxt.imp.runtime.dynamicloading.TermReader;
import org.strategoxt.imp.runtime.stratego.StrategoConsole;
import org.strategoxt.imp.runtime.stratego.adapter.IStrategoAstNode;
import org.strategoxt.lang.Context;
import org.strategoxt.stratego_aterm.aterm_escape_strings_0_0;
import org.strategoxt.stratego_aterm.pp_aterm_box_0_0;
import org.strategoxt.stratego_gpp.box2text_string_0_1;
import org.strategoxt.stratego_lib.concat_strings_0_0;
import org.strategoxt.stratego_lib.try_1_0;

/**
 * @author <NAME> <lennart add lclnet.nl>
 */
public class StrategoBuilder implements IBuilder {
	
	private final StrategoObserver observer;

	private final String caption;
	
	private String builderRule;
	
	private final boolean realTime;
	
	private final boolean openEditor;
	
	private final boolean cursor;
	
	private final boolean source;
	
	@SuppressWarnings("unused")
	private final boolean persistent;
	
	private final EditorState derivedFromEditor;
	
	/**
	 * Creates a new Stratego builder.
	 * 
	 * @param derivedFromEditor  The editor the present editor is derived from, if the present editor is an ATerm editor.
	 */
	public StrategoBuilder(StrategoObserver observer, String caption, String builderRule,
			boolean openEditor, boolean realTime, boolean cursor, boolean source, boolean persistent,
			EditorState derivedFromEditor) {
		
		this.observer = observer;
		this.caption = caption;
		this.builderRule = builderRule;
		this.openEditor = openEditor;
		this.realTime = realTime;
		this.cursor = cursor;
		this.source = source;
		this.persistent = persistent;
		this.derivedFromEditor = derivedFromEditor;
	}
	
	public String getCaption() {
		return caption;
	}
	
	public String getBuilderRule() {
		return builderRule;
	}
	
	protected StrategoObserver getObserver() {
		return observer;
	}
	
	protected EditorState getDerivedFromEditor() {
		return derivedFromEditor;
	}
	
	protected void setBuilderRule(String builderRule) {
		this.builderRule = builderRule;
	}
	
	public void execute(EditorState editor, IStrategoAstNode node, IFile errorReportFile, boolean isRebuild) {
		// TODO: refactor
		assert derivedFromEditor == null || editor.getDescriptor().isATermEditor();
		String filename = null;
		String result = null;
		String errorReport = null;
		
		synchronized (observer.getSyncRoot()) {
			try {
				if (node == null) {
					node = editor.getSelectionAst(!cursor);
					if (node == null) node = editor.getParseController().getCurrentAst();
				}
				if (node == null) {
					openError(editor, "Editor is still analyzing");
					return;
				}
				
				IStrategoTerm resultTerm = invokeObserver(node);
				if (resultTerm == null) {
					observer.reportRewritingFailed();
					Environment.logException("Builder failed:\n" + observer.getLog());
					if (!observer.isUpdateScheduled())
						observer.scheduleUpdate(editor.getParseController());
					openError(editor, "Builder failed (see error log)");
					return;
				}
		
				if (isTermAppl(resultTerm) && "None".equals(TermReader.cons(resultTerm))) {
					return;
				} else if (!isTermTuple(resultTerm) || !isTermString(termAt(resultTerm, 0))) {
					Environment.logException("Illegal builder result (must be a filename/string tuple)");
					openError(editor, "Illegal builder result (must be a filename/string tuple): " + resultTerm);
				}
	
				IStrategoTerm filenameTerm = termAt(resultTerm, 0);
				filename = asJavaString(filenameTerm);
				
				resultTerm = termAt(resultTerm, 1);
				resultTerm = try_1_0.instance.invoke(observer.getRuntime().getCompiledContext(),
						resultTerm, concat_strings_0_0.instance);
				
				if (resultTerm != null && filename != null) {
					result = isTermString(resultTerm) 
						? asJavaString(resultTerm)
						: ppATerm(resultTerm).stringValue();
				}
			} catch (InterpreterErrorExit e) {
				Environment.logException("Builder failed:\n" + observer.getLog(), e);
				if (editor.getDescriptor().isDynamicallyLoaded()) StrategoConsole.activateConsole();
				if (errorReportFile == null || !openEditor) {
					openError(editor, e.getMessage());
				} else {
					// UNDONE: Printing stack trace in editor
					// ByteArrayOutputStream trace = new ByteArrayOutputStream();
					// observer.getRuntime().getCompiledContext().printStackTrace(new PrintStream(trace), false);
					errorReport = e.getMessage();
					if (e.getTerm() != null) errorReport += "\n\t" + toEscapedString(ppATerm(e.getTerm()));
				}
			} catch (UndefinedStrategyException e) {
				reportGenericException(editor, e);
			} catch (InterpreterExit e) {
				reportGenericException(editor, e);
			} catch (InterpreterException e) {
				reportGenericException(editor, e);
			} catch (RuntimeException e) {
				reportGenericException(editor, e);
			} catch (Error e) {
				reportGenericException(editor, e);
			}
		}

		try {
			if (errorReport != null) {
				setFileContents(editor, errorReportFile, errorReport);
			}
		
			if (result != null) {
				IFile file = editor.getProject().getRawProject().getFile(filename);
				setFileContents(editor, file, result);
				// TODO: if not persistent, create IEditorInput from result String
				if (openEditor && !isRebuild) {
					IEditorPart target = openEditor(file, realTime);
					// UNDONE: don't delete non-persistent files for now since it causes problem with workspace auto-refresh
					// if (!persistent) new File(file.getLocationURI()).delete();
					// Create a listene *and* editor-derived editor relation
					StrategoBuilderListener listener = 
						StrategoBuilderListener.addListener(editor.getEditor(), target, file, getCaption(), node);
					if (!realTime || editor == target || derivedFromEditor != null)
						listener.setEnabled(false);
					if (derivedFromEditor != null) // ensure we get builders from the source
						listener.setSourceEditor(derivedFromEditor.getEditor());
				}
			}
		} catch (CoreException e) {
			Environment.logException("Builder failed", e);
			openError(editor, "Builder failed (" + e.getClass().getName() + "; see error log): " + e.getMessage());
		}
	}

	protected IStrategoTerm invokeObserver(IStrategoAstNode node) throws UndefinedStrategyException,
			InterpreterErrorExit, InterpreterExit, InterpreterException {
		
		IStrategoTerm inputTerm = derivedFromEditor != null
				? observer.makeATermInputTerm(node, true, derivedFromEditor.getResource()) 
				: observer.makeInputTerm(node, true, source);
		IStrategoTerm result = observer.invoke(builderRule, inputTerm, node.getResource());
		return result;<commit_msg>Release 0.5.1.8, incorporating the last series of minor fixes. Hopefully the final 0.5.1 release.<commit_after>package org.strategoxt.imp.runtime.services;

import static org.spoofax.interpreter.core.Tools.asJavaString;
import static org.spoofax.interpreter.core.Tools.isTermAppl;
import static org.spoofax.interpreter.core.Tools.isTermString;
import static org.spoofax.interpreter.core.Tools.isTermTuple;
import static org.spoofax.interpreter.core.Tools.termAt;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.spoofax.interpreter.core.InterpreterErrorExit;
import org.spoofax.interpreter.core.InterpreterException;
import org.spoofax.interpreter.core.InterpreterExit;
import org.spoofax.interpreter.core.UndefinedStrategyException;
import org.spoofax.interpreter.terms.IStrategoString;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.strategoxt.imp.runtime.EditorState;
import org.strategoxt.imp.runtime.Environment;
import org.strategoxt.imp.runtime.RuntimeActivator;
import org.strategoxt.imp.runtime.dynamicloading.TermReader;
import org.strategoxt.imp.runtime.stratego.StrategoConsole;
import org.strategoxt.imp.runtime.stratego.adapter.IStrategoAstNode;
import org.strategoxt.lang.Context;
import org.strategoxt.stratego_aterm.aterm_escape_strings_0_0;
import org.strategoxt.stratego_aterm.pp_aterm_box_0_0;
import org.strategoxt.stratego_gpp.box2text_string_0_1;
import org.strategoxt.stratego_lib.concat_strings_0_0;
import org.strategoxt.stratego_lib.try_1_0;

/**
 * @author <NAME> <lennart add lclnet.nl>
 */
public class StrategoBuilder implements IBuilder {
	
	private final StrategoObserver observer;

	private final String caption;
	
	private String builderRule;
	
	private final boolean realTime;
	
	private final boolean openEditor;
	
	private final boolean cursor;
	
	private final boolean source;
	
	@SuppressWarnings("unused")
	private final boolean persistent;
	
	private final EditorState derivedFromEditor;
	
	/**
	 * Creates a new Stratego builder.
	 * 
	 * @param derivedFromEditor  The editor the present editor is derived from, if the present editor is an ATerm editor.
	 */
	public StrategoBuilder(StrategoObserver observer, String caption, String builderRule,
			boolean openEditor, boolean realTime, boolean cursor, boolean source, boolean persistent,
			EditorState derivedFromEditor) {
		
		this.observer = observer;
		this.caption = caption;
		this.builderRule = builderRule;
		this.openEditor = openEditor;
		this.realTime = realTime;
		this.cursor = cursor;
		this.source = source;
		this.persistent = persistent;
		this.derivedFromEditor = derivedFromEditor;
	}
	
	public String getCaption() {
		return caption;
	}
	
	public String getBuilderRule() {
		return builderRule;
	}
	
	protected StrategoObserver getObserver() {
		return observer;
	}
	
	protected EditorState getDerivedFromEditor() {
		return derivedFromEditor;
	}
	
	protected void setBuilderRule(String builderRule) {
		this.builderRule = builderRule;
	}
	
	public void execute(EditorState editor, IStrategoAstNode node, IFile errorReportFile, boolean isRebuild) {
		// TODO: refactor
		assert derivedFromEditor == null || editor.getDescriptor().isATermEditor();
		String filename = null;
		String result = null;
		String errorReport = null;
		
		synchronized (observer.getSyncRoot()) {
			try {
				if (node == null) {
					node = editor.getSelectionAst(!cursor);
					if (node == null) node = editor.getParseController().getCurrentAst();
				}
				if (node == null) {
					openError(editor, "Editor is still analyzing");
					return;
				}
				
				IStrategoTerm resultTerm = invokeObserver(node);
				if (resultTerm == null) {
					observer.reportRewritingFailed();
					Environment.logException("Builder failed:\n" + observer.getLog());
					if (!observer.isUpdateScheduled())
						observer.scheduleUpdate(editor.getParseController());
					openError(editor, "Builder failed (see error log)");
					return;
				}
		
				if (isTermAppl(resultTerm) && "None".equals(TermReader.cons(resultTerm))) {
					return;
				} else if (!isTermTuple(resultTerm) || !isTermString(termAt(resultTerm, 0))) {
					Environment.logException("Illegal builder result (must be a filename/string tuple)");
					openError(editor, "Illegal builder result (must be a filename/string tuple): " + resultTerm);
				}
	
				IStrategoTerm filenameTerm = termAt(resultTerm, 0);
				filename = asJavaString(filenameTerm);
				
				resultTerm = termAt(resultTerm, 1);
				resultTerm = try_1_0.instance.invoke(observer.getRuntime().getCompiledContext(),
						resultTerm, concat_strings_0_0.instance);
				
				if (resultTerm != null && filename != null) {
					result = isTermString(resultTerm) 
						? asJavaString(resultTerm)
						: ppATerm(resultTerm).stringValue();
				}
			} catch (InterpreterErrorExit e) {
				Environment.logException("Builder failed:\n" + observer.getLog(), e);
				if (editor.getDescriptor().isDynamicallyLoaded()) StrategoConsole.activateConsole();
				if (errorReportFile == null || !openEditor) {
					openError(editor, e.getMessage());
				} else {
					// UNDONE: Printing stack trace in editor
					// ByteArrayOutputStream trace = new ByteArrayOutputStream();
					// observer.getRuntime().getCompiledContext().printStackTrace(new PrintStream(trace), false);
					errorReport = e.getMessage();
					if (e.getTerm() != null) errorReport += "\n\t" + toEscapedString(ppATerm(e.getTerm()));
				}
			} catch (UndefinedStrategyException e) {
				reportGenericException(editor, e);
			} catch (InterpreterExit e) {
				reportGenericException(editor, e);
			} catch (InterpreterException e) {
				reportGenericException(editor, e);
			} catch (RuntimeException e) {
				reportGenericException(editor, e);
			} catch (Error e) {
				reportGenericException(editor, e);
			}
		}

		try {
			if (errorReport != null) {
				setFileContents(editor, errorReportFile, errorReport);
			}
		
			if (result != null) {
				if (new File(filename).isAbsolute()) {
					openError(editor, "Builder failed: result filename must have a project-relative path: " + filename);
					return;
				}
				IFile file = editor.getProject().getRawProject().getFile(filename);
				setFileContents(editor, file, result);
				// TODO: if not persistent, create IEditorInput from result String
				if (openEditor && !isRebuild) {
					IEditorPart target = openEditor(file, realTime);
					// UNDONE: don't delete non-persistent files for now since it causes problem with workspace auto-refresh
					// if (!persistent) new File(file.getLocationURI()).delete();
					// Create a listene *and* editor-derived editor relation
					StrategoBuilderListener listener = 
						StrategoBuilderListener.addListener(editor.getEditor(), target, file, getCaption(), node);
					if (!realTime || editor == target || derivedFromEditor != null)
						listener.setEnabled(false);
					if (derivedFromEditor != null) // ensure we get builders from the source
						listener.setSourceEditor(derivedFromEditor.getEditor());
				}
			}
		} catch (CoreException e) {
			Environment.logException("Builder failed", e);
			openError(editor, "Builder failed (" + e.getClass().getName() + "; see error log): " + e.getMessage());
		}
	}

	protected IStrategoTerm invokeObserver(IStrategoAstNode node) throws UndefinedStrategyException,
			InterpreterErrorExit, InterpreterExit, InterpreterException {
		
		IStrategoTerm inputTerm = derivedFromEditor != null
				? observer.makeATermInputTerm(node, true, derivedFromEditor.getResource()) 
				: observer.makeInputTerm(node, true, source);
		IStrategoTerm result = observer.invoke(builderRule, inputTerm, node.getResource());
		return result;
{'fraction_non_alphanumeric': 0.10696095076400679, 'fraction_numerical': 0.002703892347355845, 'mean_word_length': 4.518390006939625, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '834576', 'n_tokens_mistral': 5484, 'n_tokens_neox': 4693, 'n_words': 1049}