<commit_before>package org.openlca.app.editors.lcia;

import java.util.stream.Collectors;

import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.openlca.app.App;
import org.openlca.app.M;
import org.openlca.app.components.ModelLink;
import org.openlca.app.db.Cache;
import org.openlca.app.db.Database;
import org.openlca.app.editors.InfoSection;
import org.openlca.app.editors.ModelEditor;
import org.openlca.app.editors.ModelPage;
import org.openlca.app.editors.lcia.geo.GeoPage;
import org.openlca.app.editors.parameters.Formulas;
import org.openlca.app.editors.parameters.ParameterChangeSupport;
import org.openlca.app.editors.parameters.ParameterPage;
import org.openlca.app.rcp.Workspace;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Controls;
import org.openlca.app.util.ErrorReporter;
import org.openlca.app.util.Labels;
import org.openlca.app.util.MsgBox;
import org.openlca.app.util.UI;
import org.openlca.app.viewers.Viewers;
import org.openlca.app.viewers.tables.TableClipboard;
import org.openlca.app.viewers.tables.Tables;
import org.openlca.core.database.usage.UsageSearch;
import org.openlca.core.model.Category;
import org.openlca.core.model.ImpactCategory;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.Source;
import org.openlca.core.model.descriptors.Descriptor;
import org.openlca.core.model.descriptors.ImpactMethodDescriptor;
import org.openlca.io.CategoryPath;
import org.openlca.util.Strings;

public class ImpactCategoryEditor extends ModelEditor<ImpactCategory> {

	public final String FACTORS_CHANGED_EVENT = "FACTORS_CHANGED_EVENT";

	private ParameterChangeSupport parameterSupport;

	public ImpactCategoryEditor() {
		super(ImpactCategory.class);
	}

	public ParameterChangeSupport getParameterSupport() {
		return parameterSupport;
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
		throws PartInitException {
		super.init(site, input);
		// evalFormulas() takes quite long; we skip this here
		parameterSupport = new ParameterChangeSupport();
		parameterSupport.onEvaluation(this::evalFormulas);

		var impact = getModel();
		if (impact.isFromLibrary()) {
			var lib = Workspace.getLibraryDir()
				.getLibrary(impact.library)
				.orElse(null);
			if (lib != null) {
				var factors = lib.getImpactFactors(
					Descriptor.of(impact),
					Database.get());
				impact.impactFactors.addAll(factors);
			}
		}
	}

	private void evalFormulas() {
		var errors = Formulas.eval(Database.get(), getModel());
		if (!errors.isEmpty()) {
			var message = errors.get(0);
			if (errors.size() > 1)
				message += " (" + (errors.size() - 1) + " more)";
			MsgBox.error(M.FormulaEvaluationFailed, message);
		}
	}

	@Override
	protected void addPages() {
		try {
			addPage(new InfoPage(this));
			addPage(new ImpactFactorPage(this));
			if (!getModel().isFromLibrary()) {
				addPage(ParameterPage.create(this));
				addPage(new GeoPage(this));
				addPage(new ImpactSimilaritiesPage(this));
				addCommentPage();
			}
		} catch (Exception e) {
			ErrorReporter.on("failed to init pages", e);
		}
	}

	static class InfoPage extends ModelPage<ImpactCategory> {

		InfoPage(ImpactCategoryEditor editor) {
			super(editor, "ImpactCategoryInfo", M.GeneralInformation);
		}

		@Override
		protected void createFormContent(IManagedForm mform) {
			var form = UI.formHeader(this);
			var tk = mform.getToolkit();
			var body = UI.formBody(form, tk);
			var info = new InfoSection(getEditor()).render(body, tk);
			var comp = info.composite();

			// source
			ModelLink.of(Source.class)
				.renderOn(comp, tk, M.Source)
				.setModel(getModel().source)
				.onChange(source -> {
					var impact = getModel();
					impact.source = source;
					getEditor().setDirty();
				});
			UI.filler(comp, tk);

			// code
			var codeText = UI.formText(comp, tk, "Code");
			Controls.set(codeText, getModel().code, code -> {
				getModel().code = code;
				getEditor().setDirty();
			});
			UI.filler(comp, tk);

			// reference unit
			var unitText = UI.formText(comp, tk, M.ReferenceUnit);
			Controls.set(unitText, getModel().referenceUnit, unit -> {
				getModel().referenceUnit = unit;
				getEditor().setDirty();
			});
			UI.filler(comp, tk);

			createUsedTable(tk, body);

			body.setFocus();
			form.reflow(true);
		}

		private void createUsedTable(FormToolkit tk, Composite body) {
			var section = UI.section(body, tk,
				"Used in impact assessment methods");
			UI.gridData(section, true, true);
			var comp = UI.sectionClient(section, tk, 1);
			var table = Tables.createViewer(comp, M.Name, M.Category);
			table.setLabelProvider(new MethodLabel());
			Tables.bindColumnWidths(table, 0.5, 0.5);

			// bind actions
			var onCopy = TableClipboard.onCopySelected(table);
			var onOpen = Actions.onOpen(() -> {
				ImpactMethodDescriptor m = Viewers.getFirstSelected(table);
				if (m != null) {
					App.open(m);
				}<commit_msg>add combo for "impact direction" of LCIA categories<commit_after>package org.openlca.app.editors.lcia;

import java.util.stream.Collectors;

import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.openlca.app.App;
import org.openlca.app.M;
import org.openlca.app.db.Cache;
import org.openlca.app.db.Database;
import org.openlca.app.editors.InfoSection;
import org.openlca.app.editors.ModelEditor;
import org.openlca.app.editors.ModelPage;
import org.openlca.app.editors.lcia.geo.GeoPage;
import org.openlca.app.editors.parameters.Formulas;
import org.openlca.app.editors.parameters.ParameterChangeSupport;
import org.openlca.app.editors.parameters.ParameterPage;
import org.openlca.app.rcp.Workspace;
import org.openlca.app.rcp.images.Images;
import org.openlca.app.util.Actions;
import org.openlca.app.util.Controls;
import org.openlca.app.util.ErrorReporter;
import org.openlca.app.util.Labels;
import org.openlca.app.util.MsgBox;
import org.openlca.app.util.UI;
import org.openlca.app.viewers.Viewers;
import org.openlca.app.viewers.tables.TableClipboard;
import org.openlca.app.viewers.tables.Tables;
import org.openlca.core.database.usage.UsageSearch;
import org.openlca.core.model.Category;
import org.openlca.core.model.Direction;
import org.openlca.core.model.ImpactCategory;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.descriptors.Descriptor;
import org.openlca.core.model.descriptors.ImpactMethodDescriptor;
import org.openlca.io.CategoryPath;
import org.openlca.util.Strings;

public class ImpactCategoryEditor extends ModelEditor<ImpactCategory> {

	public final String FACTORS_CHANGED_EVENT = "FACTORS_CHANGED_EVENT";

	private ParameterChangeSupport parameterSupport;

	public ImpactCategoryEditor() {
		super(ImpactCategory.class);
	}

	public ParameterChangeSupport getParameterSupport() {
		return parameterSupport;
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
		throws PartInitException {
		super.init(site, input);
		// evalFormulas() takes quite long; we skip this here
		parameterSupport = new ParameterChangeSupport();
		parameterSupport.onEvaluation(this::evalFormulas);

		var impact = getModel();
		if (impact.isFromLibrary()) {
			var lib = Workspace.getLibraryDir()
				.getLibrary(impact.library)
				.orElse(null);
			if (lib != null) {
				var factors = lib.getImpactFactors(
					Descriptor.of(impact),
					Database.get());
				impact.impactFactors.addAll(factors);
			}
		}
	}

	private void evalFormulas() {
		var errors = Formulas.eval(Database.get(), getModel());
		if (!errors.isEmpty()) {
			var message = errors.get(0);
			if (errors.size() > 1)
				message += " (" + (errors.size() - 1) + " more)";
			MsgBox.error(M.FormulaEvaluationFailed, message);
		}
	}

	@Override
	protected void addPages() {
		try {
			addPage(new InfoPage(this));
			addPage(new ImpactFactorPage(this));
			if (!getModel().isFromLibrary()) {
				addPage(ParameterPage.create(this));
				addPage(new GeoPage(this));
				addPage(new ImpactSimilaritiesPage(this));
				addCommentPage();
			}
		} catch (Exception e) {
			ErrorReporter.on("failed to init pages", e);
		}
	}

	static class InfoPage extends ModelPage<ImpactCategory> {

		InfoPage(ImpactCategoryEditor editor) {
			super(editor, "ImpactCategoryInfo", M.GeneralInformation);
		}

		@Override
		protected void createFormContent(IManagedForm mform) {
			var form = UI.formHeader(this);
			var tk = mform.getToolkit();
			var body = UI.formBody(form, tk);
			var info = new InfoSection(getEditor()).render(body, tk);
			var comp = info.composite();

			// source, code, reference unit
			modelLink(comp, M.Source, "source");
			text(comp, "Code", "code");
			text(comp, M.ReferenceUnit, "referenceUnit");

			// impact direction
			var combo = UI.formCombo(comp, tk, "Impact direction");
			combo.setItems("Unspecified", M.Input, M.Output);
			UI.gridData(combo, false, false).widthHint = 150;
			var dir = getModel().direction;
			combo.select(dir != null
				? dir == Direction.INPUT ? 1 : 2
				: 0);
			Controls.onSelect(combo, $ -> {
				var idx = combo.getSelectionIndex();
				getModel().direction = switch (idx) {
					case 1 -> Direction.INPUT;
					case 2 -> Direction.OUTPUT;
					default -> null;
				};
				getEditor().setDirty();
			});

			// usage
			createUsageTable(tk, body);

			body.setFocus();
			form.reflow(true);
		}

		private void createUsageTable(FormToolkit tk, Composite body) {
			var section = UI.section(body, tk,
				"Used in impact assessment methods");
			UI.gridData(section, true, true);
			var comp = UI.sectionClient(section, tk, 1);
			var table = Tables.createViewer(comp, M.Name, M.Category);
			table.setLabelProvider(new MethodLabel());
			Tables.bindColumnWidths(table, 0.5, 0.5);

			// bind actions
			var onCopy = TableClipboard.onCopySelected(table);
			var onOpen = Actions.onOpen(() -> {
				ImpactMethodDescriptor m = Viewers.getFirstSelected(table);
				if (m != null) {
					App.open(m);
				}
{'fraction_non_alphanumeric': 0.1301111739212361, 'fraction_numerical': 0.0022611644997173543, 'mean_word_length': 4.382860040567952, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, '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': '4362825', 'n_tokens_mistral': 3981, 'n_tokens_neox': 3521, 'n_words': 664}