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

org.apache.uima.tools.stylemap.StyleMapEditor Maven / Gradle / Ivy

There is a newer version: 3.5.0
Show newest version
/*
 * 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
 * 
 *   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.apache.uima.tools.stylemap;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableColumn;

import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
import org.apache.uima.cas.CAS;
import org.apache.uima.tools.images.Images;
import org.apache.uima.tools.util.gui.ImageButton;
import org.apache.uima.tools.util.htmlview.AnnotationViewGenerator;

/**
 * A GUI for editing style maps for the annotation viewer.
 * 

* A style map is an XML document that describes how each kind of output from a Text Analysis Engine * is to be displayed in the annotation viewer. Each output type that the user wants to be displayed * will have an entry in the style map. Each entry in the style map contains: *

    *
  • The CSS style used to display annotations of that type (for example "background:blue; * color:white;" to display white text on a blue background.
  • *
  • A label used to identify the annotations in the annotation viewer
  • *
*

* To invoke the editor, call the {@link #launchEditor(AnalysisEngineMetaData, String, CAS)} method. * * * */ public class StyleMapEditor extends JDialog implements ActionListener { private static final long serialVersionUID = -7774771368169207250L; private boolean buttonPress = false; private boolean populated; private AnnotationFeaturesViewer annotationFeaturesViewer; private ImageButton addTableEntryButton; private StyleMapTableModel tableModel; private StyleMapTable annotationsTable; private ArrayList styleList; private ImageButton removeTableRowButton; private ImageButton moveRowUpButton; private ImageButton moveRowDownButton; private JButton okButton = new JButton("Save"); private JButton cancelButton = new JButton("Cancel"); private JButton resetButton = new JButton("Reset"); private JDialog styleMapEditorDialog; private HashMap colorNameMap; private AnalysisEngineMetaData analysisEngineMetaData; private AnalysisEngineDescription ae; private TableGUIMediator med; // data for data model Object[][] data; /** * * Creates a new `Editor. */ public StyleMapEditor(final JFrame aOwner, CAS cas) { super(aOwner, "Style Map Editor", true); populated = false; // table not yet loaded styleList = new ArrayList(); // Save this in class member variable for use by inner-classes: styleMapEditorDialog = this; getContentPane().setLayout(new BorderLayout()); JPanel annotationFeaturesPanel = new JPanel(); annotationFeaturesPanel.setLayout(new BorderLayout()); annotationFeaturesViewer = new AnnotationFeaturesViewer(); annotationFeaturesPanel.add(annotationFeaturesViewer, BorderLayout.CENTER); JPanel iconPanel = new JPanel(); addTableEntryButton = new ImageButton(Images.FORWARD); addTableEntryButton.setToolTipText("Create stylemap table entry"); addTableEntryButton.addActionListener(this); iconPanel.add(addTableEntryButton); annotationFeaturesPanel.add(iconPanel, BorderLayout.EAST); JPanel tablePanel = new JPanel(); tablePanel.setLayout(new BorderLayout()); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, annotationFeaturesPanel, tablePanel); Dimension screenDimension = getToolkit().getScreenSize(); splitPane.setDividerLocation((int) (screenDimension.width * 0.28)); okButton.setToolTipText("Save stylemap and exit"); cancelButton.setToolTipText("Exit without saving"); resetButton.setToolTipText("Reset stylemap to that auto-generated from metadata"); JPanel buttonsPanel = new JPanel(); buttonsPanel.add(okButton); buttonsPanel.add(cancelButton); buttonsPanel.add(resetButton); getContentPane().add(splitPane, BorderLayout.CENTER); getContentPane().add(buttonsPanel, BorderLayout.SOUTH); // creating a jtable to hold the styles tableModel = new StyleMapTableModel(StyleConstants.columnNames); med = new TableGUIMediator(); annotationsTable = new StyleMapTable(tableModel, annotationFeaturesViewer, this, med); med.setTable(annotationsTable); annotationsTable.setDefaultRenderer(Color.class, new ColorRenderer(annotationsTable)); setUpColorEditor(annotationsTable); annotationsTable.setDefaultEditor(String.class, new LabelCellEditor()); final JScrollPane scrollPane = new JScrollPane(); scrollPane.getViewport().add(annotationsTable, null); tablePanel.add(scrollPane, BorderLayout.CENTER); JPanel tableButtonsPanel = new JPanel(); moveRowUpButton = new ImageButton(Images.UP); moveRowUpButton.setToolTipText("Move Row Up"); moveRowUpButton.addActionListener(this); tableButtonsPanel.add(moveRowUpButton); moveRowDownButton = new ImageButton(Images.DOWN); moveRowDownButton.setToolTipText("Move Row Down"); moveRowDownButton.addActionListener(this); tableButtonsPanel.add(moveRowDownButton); removeTableRowButton = new ImageButton(Images.ROW_DELETE); removeTableRowButton.setToolTipText("Delete Row"); removeTableRowButton.addActionListener(this); tableButtonsPanel.add(removeTableRowButton); // pass all these to the mediator which turns them on and off med.setButtons(moveRowUpButton, moveRowDownButton, removeTableRowButton); tablePanel.add(tableButtonsPanel, BorderLayout.NORTH); MyCellRenderer cellRenderer = new MyCellRenderer(); TableColumn tc = annotationsTable.getColumnModel().getColumn(0); tc.setCellRenderer(cellRenderer); TableColumn labelTableColumn = annotationsTable.getColumnModel().getColumn( StyleConstants.LABEL_COLUMN); TableColumn typeNameTableColumn = annotationsTable.getColumnModel().getColumn( StyleConstants.TYPE_NAME_COLUMN); // TableColumn featureValueTableColumn = // annotationsTable.getColumnModel().getColumn(StyleConstants.FEATURE_VALUE_COLUMN); labelTableColumn.setCellRenderer(cellRenderer); typeNameTableColumn.setCellRenderer(cellRenderer); // featureValueTableColumn.setCellRenderer(cellRenderer); // Style Edit OK Button event handle okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { Vector vn = new Vector(); for (int row = 0; row < styleList.size(); row++) { StyleMapEntry e = (StyleMapEntry) styleList.get(row); e.setAnnotationTypeName("" + tableModel.getValueAt(row, StyleConstants.TYPE_NAME_COLUMN)); e.setLabel("" + tableModel.getValueAt(row, StyleConstants.LABEL_COLUMN)); // e.setFeatureValue( "" + tableModel.getValueAt(row, // StyleConstants.FEATURE_VALUE_COLUMN)); vn.add(e.getLabel()); } buttonPress = true; Vector noDups = new Vector(); Object obj = null; for (Iterator itr = vn.iterator(); itr.hasNext();) { obj = itr.next(); if ((obj != null) && !obj.equals("")) { if (noDups.contains(obj)) { JOptionPane.showMessageDialog(StyleMapEditor.this, ("Duplicate Label : " + obj + "\n" + "Change the Label and click OK!"), "Duplicate Values", JOptionPane.PLAIN_MESSAGE); buttonPress = false; break; } else { noDups.addElement(obj); } } } if (buttonPress) styleMapEditorDialog.setVisible(false); } }); // Style Edit Exit Button Event Handling cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { buttonPress = false; styleMapEditorDialog.setVisible(false); } }); resetButton.addActionListener(this); pack(); } public Dimension getPreferredSize() { Dimension screenSize = getToolkit().getScreenSize(); Dimension d = super.getPreferredSize(); return new Dimension(screenSize.width, d.height); // return getToolkit().getScreenSize(); } /** * @deprecated use {@link #setAnalysisEngine(AnalysisEngineDescription)} instead. */ @Deprecated public void setTextAnalysisEngine(org.apache.uima.analysis_engine.TaeDescription tae) { this.ae = tae; } public void setAnalysisEngine(AnalysisEngineDescription ae) { this.ae = ae; } /** * Displays the StyleMapEditor GUI and allows the user to edit a style map. When the user has * finished, the new style map is returned. * * @param aAnalysisEngineMetaData * Metadata for the AnalysisEngine whose style map is to be edited. This contains the * AE's capabilities and type system definition, which are needed by the editor. * @param aStyleMapXml * An existing style map XML document that will be loaded into the editor. This is * optional, if null is passed in, a default style map will be automatically generated * from the AE metadata. * * @return a new style map XML document. If the user cancels, null is returned. */ public String launchEditor(AnalysisEngineMetaData aAnalysisEngineMetaData, String aStyleMapXml, CAS cas) { analysisEngineMetaData = aAnalysisEngineMetaData; // create an ArrayList of style entries used by the GUI ArrayList styleList = createStyleList(aAnalysisEngineMetaData, aStyleMapXml); // display the GUI and allow user to interact with it (modifying the styleList) if (launchGUI(styleList, cas)) { // user clicked OK, so produce a new style map XML doc from the style list return generateStyleMap(styleList); } else { // user cancelled; return null return null; } } /** * Creates a List of StyleMapEntry objects from the given AnalysisEngineMetaData and style map * XML. * * @param aAnalysisEngineMetaData * Metadata for the AnalysisEngine whose style map is being edited. * @param aStyleMapXml * An existing style map XML document. This is optional, if null is passed in, a default * style map will be automatically generated. * * @return an ArrayList containing one {@link StyleMapEntry} object for each output type declared * in aTaeMetaData. */ public ArrayList createStyleList(AnalysisEngineMetaData aAnalysisEngineMetaData, String aStyleMapXml) { styleList = new ArrayList(); // Parse the style map XML and create StyleMapEntry elements appropriately. // Note: we need to support the HTML color names as well as their hex codes; // we could do that by hardcoding a hashmap with color names as keys and // the corresponding Color objects as values. // If stylemap XML is null, auto-generate it from metadata capabilities: if (aStyleMapXml == null) aStyleMapXml = AnnotationViewGenerator.autoGenerateStyleMap(aAnalysisEngineMetaData); return parseStyleList(aStyleMapXml); } public ArrayList parseStyleList(String aStyleMapXml) { StyleMapXmlParser smxp = new StyleMapXmlParser(aStyleMapXml); // System.out.println(aStyleMapXml ); Vector annotType_SME = smxp.annotType; Vector styleLabel_SME = smxp.styleLabel; Vector featureValue_SME = smxp.featureValue; Vector styleColor_SME = smxp.styleColor; ColorParser cp = new ColorParser(); colorNameMap = cp.getColorNameMap(); for (int i = 0; i < annotType_SME.size(); i++) { String typeName = ((String) annotType_SME.elementAt(i)); String labelString = ((String) styleLabel_SME.elementAt(i)); String featureValue = ((String) featureValue_SME.elementAt(i)); String styleColor = styleColor_SME.elementAt(i).toString(); StyleMapEntry e = cp.parseAndAssignColors(typeName, featureValue, labelString, styleColor); styleList.add(e); } return styleList; } public void actionPerformed(ActionEvent evt) { JComponent source = (JComponent) evt.getSource(); if (source == moveRowUpButton) { int selectedRow = annotationsTable.getSelectedRow(); if (selectedRow == -1) { JOptionPane.showMessageDialog(source, "Select table row", "Error", JOptionPane.ERROR_MESSAGE); return; } else if (selectedRow == 0) { Toolkit.getDefaultToolkit().beep(); } else { tableModel.moveRowUp(selectedRow); ListSelectionModel lsm = annotationsTable.getSelectionModel(); int newSelectedRow = selectedRow - 1; lsm.setSelectionInterval(newSelectedRow, newSelectedRow); StyleMapEntry e = (StyleMapEntry) styleList.get(selectedRow); styleList.remove(selectedRow); styleList.add(newSelectedRow, e); } } else if (source == moveRowDownButton) { int selectedRow = annotationsTable.getSelectedRow(); if (selectedRow == -1) { JOptionPane.showMessageDialog(source, "Select table row", "Error", JOptionPane.ERROR_MESSAGE); return; } else if (selectedRow == (tableModel.getRowCount() - 1)) { Toolkit.getDefaultToolkit().beep(); } else { tableModel.moveRowDown(selectedRow); ListSelectionModel lsm = annotationsTable.getSelectionModel(); int newSelectedRow = selectedRow + 1; lsm.setSelectionInterval(newSelectedRow, newSelectedRow); StyleMapEntry e = (StyleMapEntry) styleList.get(selectedRow); styleList.remove(selectedRow); styleList.add(newSelectedRow, e); } } else if (source == addTableEntryButton) { String typeName = annotationFeaturesViewer.getSelection(); if (typeName == null) { JOptionPane.showMessageDialog(source, "You must first select an annotation type or feature", "Error", JOptionPane.ERROR_MESSAGE); return; } addRow(typeName); } else if (source == removeTableRowButton) { int row = annotationsTable.getSelectedRow(); if (row >= 0) { String message = "Are you sure you want to remove " + tableModel.getValueAt(row, StyleConstants.LABEL_COLUMN); int rv = JOptionPane.showConfirmDialog(removeTableRowButton, message, "Remove Table Row", JOptionPane.YES_NO_OPTION); if (rv == JOptionPane.YES_OPTION) { styleList.remove(row); styleList.trimToSize(); tableModel.removeRow(row); ListSelectionModel lsm = annotationsTable.getSelectionModel(); int newSelectedRow = (row == styleList.size() ? (row - 1) : row); lsm.setSelectionInterval(newSelectedRow, newSelectedRow); } } else { JOptionPane.showMessageDialog(source, "You must first select a table row to remove", "Error", JOptionPane.ERROR_MESSAGE); } } else if (source == resetButton) { styleList = createStyleList(analysisEngineMetaData, null); data = new Object[styleList.size()][StyleConstants.NR_TABLE_COLUMNS]; for (int x = 0; x < styleList.size(); x++) { StyleMapEntry e = (StyleMapEntry) styleList.get(x); data[x][0] = ""; data[x][StyleConstants.LABEL_COLUMN] = e.getLabel().trim(); data[x][StyleConstants.TYPE_NAME_COLUMN] = e.getAnnotationTypeName(); // data[x][StyleConstants.FEATURE_VALUE_COLUMN] = e.getFeatureValue(); data[x][StyleConstants.BG_COLUMN] = e.getBackground(); data[x][StyleConstants.FG_COLUMN] = e.getForeground(); data[x][StyleConstants.CHECK_COLUMN] = Boolean.valueOf(e.getChecked()); data[x][StyleConstants.HIDDEN_COLUMN] = Boolean.valueOf(e.getHidden()); } tableModel.set(data); tableModel.fireTableDataChanged(); } } protected StyleMapTable getAnnotationsTable() { return this.annotationsTable; } /** * Displays the Style Map Editor GUI and allows the user to interact with it. * * @param aStyleList * an ArrayList containing the style map entries to be edited. When the user modifies a * setting in the GUI, the elements of this List will be updated. * * @return true if the user exits the dialog by clicking the OK button, false if the user has * clicked the Cancel button. */ private boolean launchGUI(ArrayList aStyleList, CAS cas) { if (!populated) { // populate and display GUI here, then wait for user to click OK or Cancel styleList = aStyleList; data = new Object[aStyleList.size()][StyleConstants.NR_TABLE_COLUMNS]; int maxColumnWidths[] = new int[StyleConstants.NR_TABLE_COLUMNS]; maxColumnWidths[0] = 16; FontMetrics fm = annotationsTable.getFontMetrics(annotationsTable.getFont()); // maxColumnWidths[StyleConstants.FEATURE_VALUE_COLUMN] = // fm.stringWidth(StyleConstants.columnNames[StyleConstants.FEATURE_VALUE_COLUMN]); maxColumnWidths[StyleConstants.BG_COLUMN] = fm .stringWidth(StyleConstants.columnNames[StyleConstants.BG_COLUMN]); maxColumnWidths[StyleConstants.FG_COLUMN] = fm .stringWidth(StyleConstants.columnNames[StyleConstants.FG_COLUMN]); maxColumnWidths[StyleConstants.CHECK_COLUMN] = 60; maxColumnWidths[StyleConstants.HIDDEN_COLUMN] = 60; for (int x = 0; x < aStyleList.size(); x++) { StyleMapEntry e = (StyleMapEntry) aStyleList.get(x); data[x][0] = ""; data[x][StyleConstants.LABEL_COLUMN] = e.getLabel().trim(); data[x][StyleConstants.TYPE_NAME_COLUMN] = e.getAnnotationTypeName(); // data[x][StyleConstants.FEATURE_VALUE_COLUMN] = e.getFeatureValue(); data[x][StyleConstants.BG_COLUMN] = e.getBackground(); data[x][StyleConstants.FG_COLUMN] = e.getForeground(); data[x][StyleConstants.CHECK_COLUMN] = Boolean.valueOf(e.getChecked()); data[x][StyleConstants.HIDDEN_COLUMN] = Boolean.valueOf(e.getHidden()); // Calculate adequate column widths: int typeNameWidth = fm.stringWidth(e.getAnnotationTypeName()); if (typeNameWidth > maxColumnWidths[StyleConstants.TYPE_NAME_COLUMN]) maxColumnWidths[StyleConstants.TYPE_NAME_COLUMN] = typeNameWidth; int labelWidth = fm.stringWidth(e.getLabel().trim()); if (labelWidth > maxColumnWidths[StyleConstants.LABEL_COLUMN]) maxColumnWidths[StyleConstants.LABEL_COLUMN] = labelWidth; } tableModel.set(data); tableModel.fireTableDataChanged(); annotationFeaturesViewer.populate(ae, analysisEngineMetaData, cas); annotationFeaturesViewer.addTreeSelectionListener(new AnTreeListener(med)); med.setEntryButton(addTableEntryButton); // Establish sensible column widths based upon data: TableColumn column = null; for (int i = 0; i < StyleConstants.NR_TABLE_COLUMNS; i++) { column = annotationsTable.getColumnModel().getColumn(i); column.setPreferredWidth(maxColumnWidths[i] + 4); } annotationsTable.getTableHeader().repaint(); populated = true; // only load table once } styleMapEditorDialog.pack(); styleMapEditorDialog.setVisible(true); return buttonPress; } private void setUpColorEditor(JTable table) { final JTable tbl = table; // First, set up the button that brings up the dialog. final JButton button = new JButton("") { private static final long serialVersionUID = 3955120051470642157L; public void setText(String s) { // Button never shows text -- only color. } }; button.setBackground(Color.white); button.setBorderPainted(false); button.setMargin(new Insets(0, 0, 0, 0)); // Now create an editor to encapsulate the button, and // set it up as the editor for all Color cells. final ColorEditor colorEditor = new ColorEditor(button); table.setDefaultEditor(Color.class, colorEditor); // Set up the dialog that the button brings up. final JColorChooser colorChooser = new JColorChooser(); // AbstractColorChooserPanel panels[] = colorChooser.getChooserPanels(); // The following has the effect of removing the RGB panel and making the HSB panel the default: // AbstractColorChooserPanel preferredPanels[] = new AbstractColorChooserPanel[2]; // preferredPanels[0] = panels[1]; // preferredPanels[1] = panels[0]; // colorChooser.setChooserPanels(preferredPanels); ActionListener okListener = new ActionListener() { public void actionPerformed(ActionEvent e) { colorEditor.currentColor = colorChooser.getColor(); Color newColor = colorEditor.currentColor; StyleMapEntry entry = (StyleMapEntry) styleList.get(tbl.getSelectedRow()); int column = tbl.getSelectedColumn(); if (column == StyleConstants.BG_COLUMN) entry.setBackground(newColor); else entry.setForeground(newColor); tbl.repaint(); } }; final JDialog dialog = JColorChooser.createDialog(button, "Pick a Color", true, colorChooser, okListener, null); // Here's the code that brings up the dialog. button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { button.setBackground(colorEditor.currentColor); colorChooser.setColor(colorEditor.currentColor); // Without the following line, the dialog comes up // in the middle of the screen. dialog.setLocationRelativeTo(button); dialog.show(); } }); } /** * Generates a style map XML document from the style list. * * @param aStyleList * An ArrayList containing the style map entries to be written to XML. * * @return A style map XML document representing the information in aStyleList. */ private String generateStyleMap(ArrayList aStyleList) { String newStyleMap = null; try { StringBuffer newStyle = new StringBuffer(); try { newStyle.append("\n"); newStyle.append("\n"); Iterator smt = styleList.iterator(); for (int row = 0; smt.hasNext(); row++) { StyleMapEntry e = (StyleMapEntry) smt.next(); newStyle.append("\n"); newStyle.append(""); newStyle.append(e.getPattern()); newStyle.append("\n"); newStyle.append("\n"); newStyle.append("\n"); newStyle.append("\n"); } newStyle.append("\n"); } catch (Exception e) { System.out.println(e); } newStyleMap = newStyle.toString(); } catch (Throwable t) { } return newStyleMap; } public void addRow(String typeName) { // Check for duplicate annotation types that are not feature values: for (int i = 0; i < styleList.size(); i++) { StyleMapEntry e = (StyleMapEntry) styleList.get(i); if (typeName.equals(e.getAnnotationTypeName()) && typeName.indexOf(":") == -1) { JOptionPane.showMessageDialog(StyleMapEditor.this, "Duplicate Annotation Type", "Error", JOptionPane.ERROR_MESSAGE); return; } } StyleMapEntry styleMapEntry = new StyleMapEntry(); styleMapEntry.setAnnotationTypeName(typeName); styleMapEntry.setLabel(typeName); styleMapEntry.setFeatureValue(""); styleMapEntry.setForeground(Color.black); styleMapEntry.setBackground(Color.white); styleList.add(styleMapEntry); // Create a Vector of values to be held in table data model: Vector rowVector = new Vector(); rowVector.addElement(""); rowVector.addElement(typeName); rowVector.addElement(typeName); rowVector.addElement(Color.white); rowVector.addElement(Color.black); rowVector.addElement(Boolean.TRUE); rowVector.addElement(Boolean.FALSE); tableModel.addRow(rowVector); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy