code: package it.unibz.inf.ontop.protege.mapping;
  
  /*
   * #%L
   * ontop-protege
   * %%
   * Copyright (C) 2009 - 2013 KRDB Research Centre. Free University of Bozen Bolzano.
   * %%
   * 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.
   * #L%
   */
  
  import it.unibz.inf.ontop.shaded.com.google.common.collect.ImmutableSet;
  import it.unibz.inf.ontop.exception.TargetQueryParserException;
  import it.unibz.inf.ontop.protege.core.*;
  import it.unibz.inf.ontop.protege.mapping.worker.ExecuteSQLQuerySwingWorker;
  import it.unibz.inf.ontop.protege.utils.*;
  import org.apache.commons.rdf.api.IRI;
  
  import javax.annotation.Nullable;
  import javax.swing.*;
  import javax.swing.border.Border;
  import javax.swing.text.JTextComponent;
  import java.awt.*;
  import java.awt.event.ActionEvent;
  import java.awt.event.WindowEvent;
  import java.util.stream.Collectors;
  
  import static it.unibz.inf.ontop.protege.utils.DialogUtils.*;
  import static java.awt.event.KeyEvent.*;
  
  public class EditMappingDialog extends JDialog {
  
  	private static final long serialVersionUID = 4351696247473906680L;
  
  	private static final int MAX_ROWS = 100;
  
  	@Nullable
  	private final String id; // null means creating a new mapping
  
  	private final OBDAModel obdaModel;
  
  	private final TargetQueryStyledDocument targetQueryDocument;
  
  	private final JTable sqlQueryResultTable;
  	private final JTextField mappingIdField;
  	private final JTextPane targetQueryTextPane;
  	private final JTextPane sourceQueryTextPane;
  
  	private final Border defaultBorder;
  	private final Border errorBorder;
  
  	private static final int DEFAULT_TOOLTIP_INITIAL_DELAY = ToolTipManager.sharedInstance().getInitialDelay();
  	private static final int DEFAULT_TOOLTIP_DISMISS_DELAY = ToolTipManager.sharedInstance().getDismissDelay();
  	private static final int ERROR_TOOLTIP_INITIAL_DELAY = 100;
  	private static final int ERROR_TOOLTIP_DISMISS_DELAY = 9000;
  
  	private boolean allComponentsNonEmpty = false, isValid = false;
  
  	public EditMappingDialog(OBDAModel obdaModel, ColorSettings colorSettings, String id) {
  		this(obdaModel, colorSettings, null, "New Triples Map", "Create");
  
  		mappingIdField.setText(id);
  	}
  
  	public EditMappingDialog(OBDAModel obdaModel, ColorSettings colorSettings, TriplesMap triplesMap) {
  		this(obdaModel, colorSettings, triplesMap.getId(), "Edit Triples Map", "Update");
  
  		mappingIdField.setText(triplesMap.getId());
  		sourceQueryTextPane.setText(triplesMap.getSqlQuery());
  		if (triplesMap.getSqlErrorMessage() != null) {
  			sourceQueryTextPane.setToolTipText(triplesMap.getSqlErrorMessage());
  			sourceQueryTextPane.setBorder(errorBorder);
  			sourceQueryTextPane.getDocument().addDocumentListener((SimpleDocumentListener)
  					e -> sourceQueryTextPane.setBorder(defaultBorder));
  		}
  		targetQueryDocument.setInvalidPlaceholders(triplesMap.getInvalidPlaceholders());
  		targetQueryTextPane.setText(triplesMap.getTargetRendering());
  		targetValidation();
  	}
  
  	private EditMappingDialog(OBDAModel obdaModel, ColorSettings colorSettings, String id, String title, String buttonText) {
  		this.obdaModel = obdaModel;
  		this.id = id;
  
  		saveAction = new OntopAbstractAction(buttonText, null, null,
  				getKeyStrokeWithCtrlMask(VK_ENTER)) {
  			@Override
  			public void actionPerformed(ActionEvent e) {
  				saveTriplesMap();
  			}
  		};
  
  		setTitle(title);
  		setModal(true);
  
  		defaultBorder = BorderFactory.createMatteBorder(1, 1, 1, 1, Color.LIGHT_GRAY);
  		errorBorder = BorderFactory.createMatteBorder(2, 2, 2, 2, Color.RED);
  
  		JPanel mainPanel = new JPanel(new GridBagLayout());
  
  		mainPanel.add(new JLabel("Mapping ID:"),
  				new GridBagConstraints(0, 0, 1, 1, 0, 0,
  						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
  						new Insets(8, 10, 8, 10), 0, 0));
  
  		mappingIdField = new JTextField();
  		mappingIdField.setFont(TargetQueryStyledDocument.TARGET_QUERY_FONT);
  		setKeyboardShortcuts(mappingIdField);
  		mainPanel.add(mappingIdField,
  				new GridBagConstraints(1, 0, 1, 1, 0, 0,
  						GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
  						new Insets(8, 10, 8, 10), 0, 0));
  
  		JPanel targetQueryPanel = new JPanel(new BorderLayout());
  		targetQueryPanel.add(new JLabel("Target (Triples Template):"), BorderLayout.NORTH);
  		targetQueryTextPane = new JTextPane();
  		targetQueryTextPane.setBorder(defaultBorder);
  		Timer timer = new Timer(1000, e -> targetValidation());
  		timer.setRepeats(false);
  		targetQueryDocument = new TargetQueryStyledDocument(obdaModel.getObdaModelManager(), colorSettings, d -> timer.restart());
  		targetQueryTextPane.setDocument(targetQueryDocument);
  		setKeyboardShortcuts(targetQueryTextPane);
  		targetQueryPanel.add(new JScrollPane(targetQueryTextPane), BorderLayout.CENTER);
  
  		JPanel sourceQueryPanel = new JPanel(new BorderLayout());
  		sourceQueryPanel.add(new JLabel("Source (SQL Query):"), BorderLayout.NORTH);
  		sourceQueryTextPane = new JTextPane();
  		sourceQueryTextPane.setBorder(defaultBorder);
  		sourceQueryTextPane.setDocument(new SQLQueryStyledDocument());
  		setKeyboardShortcuts(sourceQueryTextPane);
  		sourceQueryPanel.add(new JScrollPane(sourceQueryTextPane), BorderLayout.CENTER);
  
  		JPanel sqlQueryResultPanel = new JPanel(new BorderLayout());
  		sqlQueryResultPanel.add(new JLabel("SQL Query results:"), BorderLayout.NORTH);
  		sqlQueryResultTable = new JTable();
  		sqlQueryResultTable.setFocusable(false);
  		sqlQueryResultPanel.add(new JScrollPane(sqlQueryResultTable), BorderLayout.CENTER);
  
  		JSplitPane sqlSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, sourceQueryPanel, sqlQueryResultPanel);
  		sqlSplitPane.setResizeWeight(0.6);
  		sqlSplitPane.setOneTouchExpandable(true);
  
  		JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, targetQueryPanel, sqlSplitPane);
  		splitPane.setResizeWeight(0.5);
  		splitPane.setOneTouchExpandable(true);
  
  		mainPanel.add(splitPane,
  				new GridBagConstraints(0, 1, 2, 1, 1, 1,
  						GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH,
  						new Insets(0, 10, 0, 10), 0, 0));
  
  		JPanel testSqlQueryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
  		testSqlQueryPanel.add(getButton(executeSqlQueryAction));
  		testSqlQueryPanel.add(new JLabel("(" + MAX_ROWS + " rows)"));
  		mainPanel.add(testSqlQueryPanel,
  				new GridBagConstraints(0, 2, 2, 1, 0, 0,
  						GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
  						new Insets(4, 10, 0, 0), 0, 0));
  
  		JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
  		JButton saveButton = getButton(saveAction);
  		controlPanel.add(saveButton);
  		controlPanel.add(getButton(cancelAction));
  		mainPanel.add(controlPanel,
  				new GridBagConstraints(0, 3, 2, 1, 0, 0,
  						GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
  						new Insets(0, 0, 10, 4), 0, 0));
  
  		setContentPane(mainPanel);
  
  		setUpAccelerator(mainPanel, saveAction);
  		setUpAccelerator(mainPanel, cancelAction);
  		setUpAccelerator(mainPanel, executeSqlQueryAction);
  		getRootPane().setDefaultButton(saveButton);
  
  		setPreferredSize(new Dimension(700, 600));
  	}
  
  	private final OntopAbstractAction saveAction;
  
  	private final OntopAbstractAction cancelAction = getStandardCloseWindowAction(CANCEL_BUTTON_TEXT, EditMappingDialog.this);
  
  	private final OntopAbstractAction executeSqlQueryAction = new OntopAbstractAction("Execute the SQL query",
  			"execute.png",
  			"Execute the SQL query in the SQL query text pane\nand display the first " + MAX_ROWS + " results in the table",
  			getKeyStrokeWithCtrlMask(VK_E)) {
  		@Override
  		public void actionPerformed(ActionEvent e) {
  			ExecuteSQLQuerySwingWorker worker = new ExecuteSQLQuerySwingWorker(
  					EditMappingDialog.this,
  					obdaModel.getDataSource(),
  					sourceQueryTextPane.getText().trim(),
  					MAX_ROWS,
  					sqlQueryResultTable::setModel);
  			worker.execute();
  		}
  	};
  
  	private void setKeyboardShortcuts(JTextComponent component) {
  		component.setFocusTraversalKeys(
  				KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
  				ImmutableSet.of(KeyStroke.getKeyStroke(VK_TAB, 0)));
  		component.setFocusTraversalKeys(
  				KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
  				ImmutableSet.of(KeyStroke.getKeyStroke(VK_TAB, SHIFT_DOWN_MASK)));
  
  		component.getDocument().addDocumentListener((SimpleDocumentListener)
  				e -> updateSaveMappingActionStatus());
  	}
  
  	private void updateSaveMappingActionStatus() {
  		allComponentsNonEmpty = !mappingIdField.getText().trim().isEmpty()
  				&& !targetQueryTextPane.getText().trim().isEmpty()
  				&& !sourceQueryTextPane.getText().trim().isEmpty();
  
  		saveAction.setEnabled(isValid && allComponentsNonEmpty);
  	}
  
  	private void targetValidation() {
  		ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
  		String error;
  		try {
  			ImmutableSet<IRI> iris = targetQueryDocument.validate();
  			if (iris.isEmpty()) {
  				if (!isValid) {
  					isValid = true;
  					saveAction.setEnabled(allComponentsNonEmpty);
  					targetQueryTextPane.setToolTipText(null);
  					targetQueryTextPane.setBorder(defaultBorder);
  					toolTipManager.setInitialDelay(DEFAULT_TOOLTIP_INITIAL_DELAY);
  					toolTipManager.setDismissDelay(DEFAULT_TOOLTIP_DISMISS_DELAY);
  				}
  				return;
  			}
  			OntologyPrefixManager prefixManager = obdaModel.getMutablePrefixManager();
  			error = iris.stream()
  					.map(IRI::getIRIString)
  					.map(prefixManager::getShortForm)
  					.map(iri -> "\t- " + iri)
  					.collect(Collectors.joining(
  							",\n",
  							"The following predicates are not declared in the ontology:\n",
  							"."));
  		}
  		catch (TargetQueryParserException e) {
  			error = (e.getMessage() == null)
  					? "Unknown syntax error, check Protege log file."
  					: e.getMessage().replace("'<EOF>'", "the end");
  		}
  		targetQueryTextPane.setBorder(errorBorder);
  		toolTipManager.setInitialDelay(ERROR_TOOLTIP_INITIAL_DELAY);
  		toolTipManager.setDismissDelay(ERROR_TOOLTIP_DISMISS_DELAY);
  		targetQueryTextPane.setToolTipText("<html>" + htmlEscape(error) + "</html>");
  		isValid = false;
  		saveAction.setEnabled(false);
  	}
  
  	private void saveTriplesMap() {
  		try {
  			String newId = mappingIdField.getText().trim();
  			String target = targetQueryTextPane.getText();
  			String source = sourceQueryTextPane.getText().trim();
  
  			if (id == null)
  				obdaModel.getTriplesMapManager().add(newId, source, target);
  			else
  				obdaModel.getTriplesMapManager().update(id, newId, source, target);
  
  			dispatchEvent(new WindowEvent(EditMappingDialog.this, WindowEvent.WINDOW_CLOSING));
  		}
  		catch (DuplicateTriplesMapException e) {
  			JOptionPane.showMessageDialog(this, "Error while inserting triples map: ID " + e.getMessage() + " is already taken");
  		}
  		catch (Exception e) {
  			JOptionPane.showMessageDialog(this, "Error while inserting triples map: " + e.getMessage());
  		}
  	}
  }
  