content: /*****************************************************************
   *   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
   *
   *    https://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.apache.cayenne.modeler.editor;
  
  import com.jgoodies.forms.builder.PanelBuilder;
  import com.jgoodies.forms.layout.CellConstraints;
  import com.jgoodies.forms.layout.FormLayout;
  import org.apache.cayenne.configuration.event.QueryEvent;
  import org.apache.cayenne.map.QueryDescriptor;
  import org.apache.cayenne.map.SQLTemplateDescriptor;
  import org.apache.cayenne.modeler.ProjectController;
  import org.apache.cayenne.modeler.util.DbAdapterInfo;
  import org.apache.cayenne.modeler.util.JUndoableCayenneTextPane;
  import org.apache.cayenne.swing.components.textpane.JCayenneTextPane;
  import org.apache.cayenne.swing.components.textpane.syntax.SQLSyntaxConstants;
  import org.apache.cayenne.util.Util;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import javax.swing.DefaultComboBoxModel;
  import javax.swing.DefaultListCellRenderer;
  import javax.swing.JList;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.ListSelectionModel;
  import javax.swing.event.DocumentEvent;
  import javax.swing.event.DocumentListener;
  import javax.swing.event.ListSelectionEvent;
  import javax.swing.event.ListSelectionListener;
  import javax.swing.text.BadLocationException;
  import javax.swing.text.Document;
  import java.awt.BorderLayout;
  import java.awt.Color;
  import java.awt.Component;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  
  /**
   * A panel for configuring SQL scripts of a SQL template.
   *
   */
  public class SQLTemplateScriptsTab extends JPanel {
  
      private static final String DEFAULT_LABEL = "Default";
      private static final Logger logger = LoggerFactory.getLogger(SQLTemplateScriptsTab.class);
  
      protected ProjectController mediator;
  
      protected JList<String> scripts;
      protected List<String> keys;
      protected PanelBuilder builder;
      protected CellConstraints cc;
      protected JCayenneTextPane textPane;
      protected List<JCayenneTextPane> panes;
      protected ListSelectionListener scriptRefreshHandler;
  
      public SQLTemplateScriptsTab(ProjectController mediator) {
          this.mediator = mediator;
  
          initView();
      }
  
      private void prepareScriptAreas() {
          for(String key : DbAdapterInfo.getStandardAdapters()) {
              JCayenneTextPane currPane = new JUndoableCayenneTextPane(new SQLSyntaxConstants());
              currPane.setName(key);
              currPane.getDocument().addDocumentListener(new CustomListener(currPane.getName()));
              builder.add(currPane.getScrollPane(), cc.xy(3, 2));
              panes.add(currPane);
          }
      }
  
      protected void initView() {
          // create widgets, etc.
  
          scriptRefreshHandler = new ListSelectionListener() {
  
              public void valueChanged(ListSelectionEvent e) {
                  if (!e.getValueIsAdjusting()) {
                      displayScript();
                  }
              }
          };
  
          scripts = new JList<>();
          scripts.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
          scripts.setCellRenderer(new DbAdapterListRenderer(DbAdapterInfo.getStandardAdapterLabels()));
  
          keys = new ArrayList<>(DbAdapterInfo.getStandardAdapters().length + 1);
          keys.addAll(Arrays.asList(DbAdapterInfo.getStandardAdapters()));
          Collections.sort(keys);
          keys.add(0, DEFAULT_LABEL);
          scripts.setModel(new DefaultComboBoxModel<>(keys.toArray(new String[0])));
  
          // assemble
          cc = new CellConstraints();
  
          textPane = new JUndoableCayenneTextPane(new SQLSyntaxConstants());
          textPane.setName(DEFAULT_LABEL);
          textPane.getDocument().addDocumentListener(new CustomListener(textPane.getName()));
  
          panes = new ArrayList<>();
          panes.add(textPane);
  
          builder = new PanelBuilder(new FormLayout(
                  "fill:100dlu, 3dlu, fill:100dlu:grow",
                  "3dlu, fill:100dlu:grow"));
  
          // orderings table must grow as the panel is resized
          builder.add(new JScrollPane(
                  scripts,
                  JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                  JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), cc.xy(1, 2));
          builder.add(textPane.getScrollPane(), cc.xy(3, 2));
  
          setLayout(new BorderLayout());
          add(builder.getPanel(), BorderLayout.CENTER);
  
          prepareScriptAreas();
      }
  
      void initFromModel() {
          QueryDescriptor query = mediator.getCurrentQuery();
  
          if (query == null || !QueryDescriptor.SQL_TEMPLATE.equals(query.getType())) {
              setVisible(false);
              return;
          }
  
          // select default script.. display it bypassing the listener...
          scripts.removeListSelectionListener(scriptRefreshHandler);
          scripts.setSelectedIndex(0);
          displayScript();
          scripts.addListSelectionListener(scriptRefreshHandler);
  
          setVisible(true);
      }
  
      /**
       * Returns SQLTemplate text for current selection.
       */
      String getSQLTemplate(String key) {
          if (key == null) {
              return null;
          }
  
          SQLTemplateDescriptor query = getQuery();
          if (query == null) {
              return null;
          }
  
          return (key.equals(DEFAULT_LABEL)) ? query.getSql() : query
                  .getAdapterSql().get(key);
      }
  
      SQLTemplateDescriptor getQuery() {
          QueryDescriptor query = mediator.getCurrentQuery();
          return (query != null && QueryDescriptor.SQL_TEMPLATE.equals(query.getType())) ?
                  (SQLTemplateDescriptor) query : null;
      }
  
      /**
       * Shows selected script in the editor.
       */
      void displayScript() {
  
          SQLTemplateDescriptor query = getQuery();
          if (query == null) {
              return;
          }
  
          String key = scripts.getSelectedValue();
          if (key == null) {
              return;
          }
  
          final String text = (key.equals(DEFAULT_LABEL)) ? query.getSql() : query
                  .getAdapterSql().get(key);
  
          for (final JCayenneTextPane textPane : panes) {
              if (key.equals(textPane.getName())) {
                  textPane.setDocumentTextDirect(text);
                  textPane.getScrollPane().setVisible(true);
                  textPane.getPane().requestFocusInWindow();
              } else {
                  textPane.getScrollPane().setVisible(false);
              }
          }
      }
  
      void setSQL(DocumentEvent e, String key) {
          if (key == null) {
              return;
          }
  
          SQLTemplateDescriptor query = getQuery();
          if (query == null) {
              return;
          }
  
          Document doc = e.getDocument();
          String text = null;
  
          try {
              text = doc.getText(0, doc.getLength());
          } catch (BadLocationException ex) {
              logger.warn("Error reading document", ex);
          }
  
          if (text != null) {
              text = text.trim();
              if (text.length() == 0) {
                  text = null;
              }
          }
  
          // Compare the value before modifying the query - text pane
          // will call "verify" even if no changes have occured....
          if (key.equals(DEFAULT_LABEL)) {
              if (!Util.nullSafeEquals(text, query.getSql())) {
                  query.setSql(text);
                  mediator.fireQueryEvent(new QueryEvent(this, query));
              }
          } else {
              if (!Util.nullSafeEquals(text, query.getAdapterSql().get(key))) {
                  query.getAdapterSql().put(key, text);
                  mediator.fireQueryEvent(new QueryEvent(this, query));
              }
          }
      }
  
      final class CustomListener implements DocumentListener{
  
          private String key;
  
          public CustomListener(String key) {
              this.key = key;
          }
  
          @Override
          public void insertUpdate(DocumentEvent e) {
              changedUpdate(e);
          }
  
          @Override
          public void removeUpdate(DocumentEvent e) {
              changedUpdate(e);
          }
  
          @Override
          public void changedUpdate(DocumentEvent e) {
              setSQL(e, key);
          }
      }
  
      final class DbAdapterListRenderer extends DefaultListCellRenderer {
  
          Map adapterLabels;
  
          DbAdapterListRenderer(Map adapterLabels) {
              this.adapterLabels = (adapterLabels != null)
                      ? adapterLabels
                      : Collections.EMPTY_MAP;
          }
  
          public Component getListCellRendererComponent(
                  JList list,
                  Object object,
                  int index,
                  boolean selected,
                  boolean hasFocus) {
  
              if (object instanceof Class) {
                  object = ((Class) object).getName();
              }
  
              Object label = adapterLabels.get(object);
              if (label == null) {
                  label = object;
              }
  
              Component c = super.getListCellRendererComponent(
                      list,
                      label,
                      index,
                      selected,
                      hasFocus);
  
              // grey out keys that have no SQL
              setForeground(selected || getSQLTemplate(object.toString()) != null
                      ? Color.BLACK
                      : Color.LIGHT_GRAY);
  
              return c;
          }
      }
  
      public int getSelectedIndex() {
          return scripts.getSelectedIndex();
      }
  
      public void setSelectedIndex(int index) {
          scripts.setSelectedIndex(index);
      }
  }
  