package com.monead.semantic.education; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.AbstractTableModel; import javax.swing.text.AttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.tree.*; import org.apache.log4j.Logger; import org.mindswap.pellet.jena.PelletReasonerFactory; import org.mindswap.pellet.utils.VersionInfo; import com.hp.hpl.jena.ontology.Individual; import com.hp.hpl.jena.ontology.OntClass; import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntModelSpec; import com.hp.hpl.jena.query.ARQ; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; import com.hp.hpl.jena.query.QueryFactory; import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Statement; import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.reasoner.Reasoner; import com.hp.hpl.jena.util.iterator.ExtendedIterator; /** * SemanticUI - A GUI to input assertions, run an inferencing engine and use * SPARQL queries on the resulting model * * This program uses Jena and Pellet to provide the inference support. * * Copyright (C) 2010 David S. Read * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * For information on Jena: http://jena.sourceforge.net/ For information on * Pellet: http://clarkparsia.com/pellet * * @author David Read * * TODO Add support for reading and writing RDF from URLs * * TODO Add support to write inferred triples to a file * * TODO Add support for SPARQL queries that use the model and URLs * * TODO Store settings in properties file */ public class SemanticUI extends JFrame implements Runnable, WindowListener { /** * The version identifier */ public final static String VERSION = "1.2"; /** * Serial UID to keep environment happy */ private final static long serialVersionUID = 19991231; /** * The set of formats that can be loaded. These are defined by Jena */ private final static String[] FORMATS = { "N3", "N-Triples", "RDF/XML", "Turtle" }; /** * Logger Instance */ private static Logger LOGGER = Logger.getLogger(SemanticUI.class); /** * Standard prefixes * * In N3, N-Triples, RDF/XML and Turtle Order matches that of the FORMAT * array */ private final static String[][] STANDARD_PREFIXES = { // N3 { "@prefix rdf: .", "@prefix rdfs: .", "@prefix owl: .", "@prefix xsd: .", "@prefix dc: .", }, // N-triples {}, // RDF/XML { "" }, // Turtle { "@prefix rdf: .", "@prefix rdfs: .", "@prefix owl: .", "@prefix xsd: .", "@prefix dc: .", }, // SPARQL { "prefix rdf: ", "prefix rdfs: ", "prefix owl: ", "prefix xsd: ", "prefix dc: ", } }; /** * The set of reasoning levels that will be compared. */ protected final static String[] REASONING_LEVELS = { "none", "rdfs", "owl" }; /** * Constant used if a value cannot be found in an array */ private final static int UNKNOWN = -1; /** * The name (and path if necessary) to the ontology being loaded */ private File rdfFile; /** * The processed ontology */ private OntModel ontModel; /** * The asserted model (no inferred information) */ private OntModel ontModelNoInference; /** * File open menu item * * Used to load an ontology in to the assertions text area */ private JMenuItem fileOpen; /** * File save menu item * * Used to save the ontology text to a file */ private JMenuItem fileSave; /** * File save menu item * * Used to serialize the current model to a file */ private JMenuItem fileSaveSerializedModel; /** * End the program */ private JMenuItem fileExit; /** * Edit insert prefixes menu item * * Used to insert standard prefixes as a convenience */ private JMenuItem editInsertPrefixes; /** * Expand all the nodes of the tree view of the model */ private JMenuItem editExpandAllTreeNodes; /** * Collapse all the nodes of the tree view of the model */ private JMenuItem editCollapseAllTreeNodes; /** * The format to use when writing the RDF to a file */ private JCheckBoxMenuItem[] setupOutputAssertionLanguage; /** * Write only assertions when outputting the model */ private JCheckBoxMenuItem setupOutputModelTypeAssertions; /** * Write assertions and inferences when outputting the model */ private JCheckBoxMenuItem setupOutputModelTypeAssertionsAndInferences; /** * Set the font used for the major interface display widgets */ private JMenuItem setupFont; /** * View the about dialog */ private JMenuItem helpAbout; /** * Allows selection of the reasoning level */ private JComboBox reasoningLevel; /** * Allows selection of the semantic syntax being used */ private JComboBox language; /** * The semantic syntax used for the assertions * * Set when the assertions are loaded into the model */ private String assertionLanguage; /** * Run the inferencing engine */ private JButton runInferencing; /** * Execute the SPARQL query */ private JButton runSparql; /** * The assertions */ private JTextArea assertions; /** * SPARQL input */ private JTextArea sparqlInput; /** * Choose SPARQL service to use */ private JComboBox sparqlServiceUrl; /** * SPARQL execution results */ private JTable sparqlResultsTable; /** * Main work area housing the assertions, output and SPARQL text areas */ private JTabbedPane tabbedPane; /** * Status reporting */ private JLabel status; /** * The results from running the inferencing engine */ private JTextArea results; /** * The resulting model displayed as a tree */ private JTree ontModelTree; /** * Should the reasoner be run (otherwise SPARQL query is being run) * * TODO prevent collision (e.g. lock execution) */ private boolean isRunReasoner; /** * Constructor - sets up the UI */ public SemanticUI() { LOGGER.info("Startup"); addWindowListener(this); setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setTitle("Semantic UI"); setupGUI(); enableControls(true); pack(); setStatus(""); setVisible(true); } /** * Place the components in the JFrame */ private void setupGUI() { JPanel panel; LOGGER.debug("SetupGUI"); setupControls(); setupMenus(); getContentPane().setLayout(new BorderLayout()); panel = new JPanel(); panel.setLayout(new BorderLayout()); getContentPane().add(panel, BorderLayout.CENTER); tabbedPane = new JTabbedPane(); // Assertions tabbedPane.add("Assertions", setupAssertionsPanel()); // Inferences tabbedPane.add("Inferences", setupInferencesPanel()); // Tree tabbedPane.add("Tree View", setupTreePanel()); // SPARQL tabbedPane.add("SPARQL", setupSparqlPanel()); // Add the tabbed pane to the main window panel.add(tabbedPane, BorderLayout.CENTER); // Status label, bottom of window panel.add(setupStatusPanel(), BorderLayout.SOUTH); } /** * Present the user with a font selection dialog and update the widgets if * the user chooses a font. */ private void configureFont() { FontChooser chooser; Font newFont; Color newColor; FontMetrics fontMetrics; chooser = new FontChooser(this); LOGGER.debug("Font before choices: " + chooser.getNewFont()); chooser.setVisible(true); newFont = chooser.getNewFont(); newColor = chooser.getNewColor(); LOGGER.debug("Font after choices: " + newFont); if (newFont != null) { assertions.setFont(newFont); results.setFont(newFont); sparqlInput.setFont(newFont); ontModelTree.setFont(newFont); sparqlResultsTable.setFont(newFont); sparqlResultsTable.getTableHeader().setFont(newFont); fontMetrics = sparqlResultsTable.getFontMetrics(newFont); sparqlResultsTable.setRowHeight(((int) ((double) fontMetrics .getHeight() * 1.1))); status.setFont(newFont); } if (newColor != null) { assertions.setForeground(newColor); results.setForeground(newColor); sparqlInput.setForeground(newColor); ontModelTree.setForeground(newColor); ((DefaultTreeCellRenderer) ontModelTree.getCellRenderer()) .setTextNonSelectionColor(newColor); sparqlResultsTable.setForeground(newColor); sparqlResultsTable.getTableHeader().setForeground(newColor); status.setForeground(newColor); } } /** * Create the assertions panel * * @return The assertions JPanel */ private JPanel setupAssertionsPanel() { JPanel assertionPanel; JPanel gridPanel; JPanel flowPanel; assertionPanel = new JPanel(); assertionPanel.setLayout(new BorderLayout()); // Top of panel will allow for configuration of // inferencing environment gridPanel = new JPanel(); gridPanel.setLayout(new GridLayout(0, 2)); // First Row (reasoning level and input language) flowPanel = new JPanel(); flowPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); flowPanel.add(new JLabel("Reasoning Level:")); flowPanel.add(reasoningLevel); gridPanel.add(flowPanel); // Language drop-down flowPanel = new JPanel(); flowPanel.setLayout(new FlowLayout()); flowPanel.add(new JLabel("Language:")); flowPanel.add(language); gridPanel.add(flowPanel); // Second Row gridPanel.add(makeFlowPanel(new JLabel("Enter Assertions"), FlowLayout.LEFT)); flowPanel = new JPanel(); flowPanel.setLayout(new FlowLayout()); flowPanel.add(runInferencing); gridPanel.add(flowPanel); assertionPanel.add(gridPanel, BorderLayout.NORTH); assertionPanel.add(new JScrollPane(assertions), BorderLayout.CENTER); return assertionPanel; } /** * Setup the inferences panel * * @return The inferences JPanel */ private JPanel setupInferencesPanel() { JPanel inferencesPanel; // output inferencesPanel = new JPanel(); inferencesPanel.setLayout(new GridLayout(1, 1)); inferencesPanel.add(new JScrollPane(results)); return inferencesPanel; } /** * Setup the model tree display panel * * @return The model tree JPanel */ private JPanel setupTreePanel() { JPanel treePanel; treePanel = new JPanel(); treePanel.setLayout(new GridLayout(1, 1)); treePanel.add(new JScrollPane(ontModelTree)); return treePanel; } /** * Setup the SPARQL panel * * @return The SPARQL JPanel */ private JPanel setupSparqlPanel() { JPanel sparqlPanel; JPanel labelPanel; JPanel gridPanel; JPanel innerGridPanel; JPanel flowPanel; sparqlPanel = new JPanel(); sparqlPanel.setLayout(new GridLayout(2, 1)); // SPARQL Input labelPanel = new JPanel(); labelPanel.setLayout(new BorderLayout()); gridPanel = new JPanel(); gridPanel.setLayout(new GridLayout(2, 1)); innerGridPanel = new JPanel(); innerGridPanel.setLayout(new GridLayout(1, 2)); innerGridPanel.add(new JLabel("SPARQL Query")); flowPanel = new JPanel(); flowPanel.setLayout(new FlowLayout()); flowPanel.add(runSparql); innerGridPanel.add(flowPanel); gridPanel.add(innerGridPanel); flowPanel = new JPanel(); flowPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); flowPanel.add(new JLabel("SPARQL Service: ")); flowPanel.add(sparqlServiceUrl); gridPanel.add(flowPanel); labelPanel.add(gridPanel, BorderLayout.NORTH); labelPanel.add(new JScrollPane(sparqlInput), BorderLayout.CENTER); sparqlPanel.add(labelPanel); // SPARQL results labelPanel = new JPanel(); labelPanel.setLayout(new BorderLayout()); labelPanel.add(new JLabel("Results"), BorderLayout.NORTH); // labelPanel.add(new JScrollPane(sparqlResults), BorderLayout.CENTER); labelPanel .add(new JScrollPane(sparqlResultsTable), BorderLayout.CENTER); sparqlPanel.add(labelPanel); return sparqlPanel; } /** * Setup the status panel * * @return The status JPanel */ private JPanel setupStatusPanel() { JPanel statusPanel; statusPanel = new JPanel(); statusPanel.setLayout(new GridLayout(1, 1)); statusPanel.add(makeFlowPanel(status, FlowLayout.LEFT)); return statusPanel; } /** * Setup the frame's menus */ private void setupMenus() { JMenuBar menuBar; JMenu menu; ButtonGroup buttonGroup; menuBar = new JMenuBar(); setJMenuBar(menuBar); menu = new JMenu("File"); menu.setMnemonic(KeyEvent.VK_F); menu.getAccessibleContext().setAccessibleDescription( "Menu items related to file access"); menuBar.add(menu); fileOpen = new JMenuItem("Open"); fileOpen.setMnemonic('O'); fileOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK)); fileOpen.setMnemonic(KeyEvent.VK_L); fileOpen.getAccessibleContext().setAccessibleDescription( "Open an ontology file"); fileOpen.addActionListener(new FileOpenListener()); menu.add(fileOpen); menu.addSeparator(); fileSave = new JMenuItem("Save Assertions Text"); fileSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.ALT_MASK)); fileSave.setMnemonic(KeyEvent.VK_S); fileSave.getAccessibleContext().setAccessibleDescription( "Write the assertions to a file"); fileSave.addActionListener(new FileSaveListener()); menu.add(fileSave); fileSaveSerializedModel = new JMenuItem("Save Model"); fileSaveSerializedModel.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_M, ActionEvent.ALT_MASK)); fileSaveSerializedModel.setMnemonic(KeyEvent.VK_M); fileSaveSerializedModel.getAccessibleContext() .setAccessibleDescription( "Write an ontology file from the current model"); fileSaveSerializedModel .addActionListener(new ModelSerializerListener()); menu.add(fileSaveSerializedModel); menu.addSeparator(); fileExit = new JMenuItem("Exit"); fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.ALT_MASK)); fileExit.setMnemonic(KeyEvent.VK_X); fileExit.getAccessibleContext().setAccessibleDescription( "Exit the application"); fileExit.addActionListener(new EndApplicationListener()); menu.add(fileExit); // Edit Menu menu = new JMenu("Edit"); menu.setMnemonic(KeyEvent.VK_E); menu.getAccessibleContext().setAccessibleDescription( "Menu items releated to editing the ontology"); menuBar.add(menu); editInsertPrefixes = new JMenuItem("Insert Prefixes"); editInsertPrefixes.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, ActionEvent.ALT_MASK)); editInsertPrefixes.setMnemonic(KeyEvent.VK_I); editInsertPrefixes.getAccessibleContext().setAccessibleDescription( "Insert standard prefixes (namespaces)"); editInsertPrefixes.addActionListener(new InsertPrefixesListener()); menu.add(editInsertPrefixes); menu.addSeparator(); editExpandAllTreeNodes = new JMenuItem("Expand Entire Tree"); editExpandAllTreeNodes.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_ADD, ActionEvent.ALT_MASK)); editExpandAllTreeNodes.setMnemonic(KeyEvent.VK_E); editExpandAllTreeNodes.getAccessibleContext().setAccessibleDescription( "Expand all tree nodes"); editExpandAllTreeNodes.addActionListener(new ExpandTreeListener()); menu.add(editExpandAllTreeNodes); editCollapseAllTreeNodes = new JMenuItem("Collapse Entire Tree"); editCollapseAllTreeNodes.setAccelerator(KeyStroke.getKeyStroke( KeyEvent.VK_SUBTRACT, ActionEvent.ALT_MASK)); editCollapseAllTreeNodes.setMnemonic(KeyEvent.VK_C); editCollapseAllTreeNodes.getAccessibleContext() .setAccessibleDescription("Expand all tree nodes"); editCollapseAllTreeNodes.addActionListener(new CollapseTreeListener()); menu.add(editCollapseAllTreeNodes); // Setup Menu menu = new JMenu("Setup"); menu.setMnemonic(KeyEvent.VK_S); menu.getAccessibleContext().setAccessibleDescription( "Menu items related to configuration"); menuBar.add(menu); buttonGroup = new ButtonGroup(); setupOutputAssertionLanguage = new JCheckBoxMenuItem[FORMATS.length + 1]; setupOutputAssertionLanguage[0] = new JCheckBoxMenuItem( "Output Format: Auto"); buttonGroup.add(setupOutputAssertionLanguage[0]); menu.add(setupOutputAssertionLanguage[0]); for (int index = 0; index < FORMATS.length; ++index) { setupOutputAssertionLanguage[index + 1] = new JCheckBoxMenuItem( "Output Format: " + FORMATS[index]); buttonGroup.add(setupOutputAssertionLanguage[index + 1]); menu.add(setupOutputAssertionLanguage[index + 1]); } setupOutputAssertionLanguage[0].setSelected(true); menu.addSeparator(); buttonGroup = new ButtonGroup(); setupOutputModelTypeAssertions = new JCheckBoxMenuItem( "Output Assertions Only"); buttonGroup.add(setupOutputModelTypeAssertions); menu.add(setupOutputModelTypeAssertions); setupOutputModelTypeAssertionsAndInferences = new JCheckBoxMenuItem( "Output Assertions and Inferences"); buttonGroup.add(setupOutputModelTypeAssertionsAndInferences); menu.add(setupOutputModelTypeAssertionsAndInferences); setupOutputModelTypeAssertions.setSelected(true); menu.addSeparator(); setupFont = new JMenuItem("Font"); setupFont.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, ActionEvent.ALT_MASK)); setupFont.setMnemonic(KeyEvent.VK_F); setupFont.getAccessibleContext().setAccessibleDescription( "Set the font used for the display"); setupFont.addActionListener(new FontSetupListener()); menu.add(setupFont); // Help Menu menu = new JMenu("Help"); menu.setMnemonic(KeyEvent.VK_H); menu.getAccessibleContext().setAccessibleDescription( "Menu items releated to user assistance"); menuBar.add(menu); helpAbout = new JMenuItem("About"); helpAbout.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, ActionEvent.ALT_MASK)); helpAbout.setMnemonic(KeyEvent.VK_H); helpAbout.getAccessibleContext().setAccessibleDescription( "View version information"); helpAbout.addActionListener(new AboutListener()); menu.add(helpAbout); } /** * Enable and disable controls based on current state * * @param enable * Whether to enable or disable controls */ private void enableControls(boolean enable) { String sparqlService; LOGGER.debug("Called enableControls with setting " + enable); assertions.setEditable(enable); sparqlInput.setEditable(enable); fileOpen.setEnabled(enable); if (enable && ontModelNoInference != null) { fileSaveSerializedModel.setEnabled(true); } else { fileSaveSerializedModel.setEnabled(false); } if (enable && assertions.getText().trim().length() > 0) { runInferencing.setEnabled(true); fileSave.setEnabled(true); } else { runInferencing.setEnabled(false); fileSave.setEnabled(false); } sparqlService = ((String) sparqlServiceUrl.getEditor().getItem()) .trim(); if (!sparqlService.equals((String) sparqlServiceUrl.getItemAt(0)) && sparqlService.length() > 0 && sparqlInput.getText().trim().length() > 0) { runSparql.setEnabled(true); LOGGER.debug("Enabled Run SPARQL Button (" + sparqlService + ")"); } else if (enable && sparqlInput.getText().trim().length() > 0 && ontModel != null) { runSparql.setEnabled(true); LOGGER.debug("Enabled Run SPARQL Button (" + sparqlService + ")"); } else { runSparql.setEnabled(false); LOGGER.debug("Disabled Run SPARQL Button (" + sparqlService + ")"); } } /** * Setup all the components */ private void setupControls() { LOGGER.debug("setupControls"); reasoningLevel = new JComboBox(); for (String level : REASONING_LEVELS) { reasoningLevel.addItem(level); } reasoningLevel.setSelectedIndex(reasoningLevel.getItemCount() - 1); language = new JComboBox(); language.addItem("Auto"); for (String lang : FORMATS) { language.addItem(lang); } language.setSelectedIndex(0); runInferencing = new JButton("Run Reasoner"); runInferencing.addActionListener(new ReasonerListener()); runSparql = new JButton("Run SPARQL"); runSparql.addActionListener(new SparqlListener()); assertions = new JTextArea(10, 50); assertions.addKeyListener(new UserInputListener()); results = new JTextArea(10, 50); results.setEditable(false); // SPARQL Input sparqlInput = new JTextArea(10, 50); sparqlInput.addKeyListener(new UserInputListener()); sparqlServiceUrl = new JComboBox(); sparqlServiceUrl.setEditable(true); sparqlServiceUrl.addItem("Local Model"); sparqlServiceUrl.addItem("http://DBpedia.org/sparql"); sparqlServiceUrl.addItem("http://lod.openlinksw.com/sparql/"); sparqlServiceUrl.addItem("http://semantic.data.gov/sparql"); sparqlServiceUrl .addItem("http://www4.wiwiss.fu-berlin.de/gutendata/sparql"); sparqlServiceUrl.addActionListener(new SparqlModelChoiceListener()); sparqlServiceUrl.getEditor().getEditorComponent().addKeyListener( new UserInputListener()); // A basic default query sparqlInput.setText("select ?s ?p ?o where { ?s ?p ?o }"); sparqlResultsTable = new JTable(new SparqlTableModel()); ontModelTree = new JTree(new DefaultTreeModel( new DefaultMutableTreeNode("No Model Created"))); status = new JLabel("Initializing"); status.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12)); } /** * Create a JPanel that uses a FlowLayout and add a component to the JPanel. * The method creates a JPanel, sets its layout to FlowLayout and adds the * supplied component to itself. * * @param component * The component to be placed using a FlowLayout * @param alignment * How to align the component. Use a FlowLayout constant. * * @return The new JPanel instance */ private JPanel makeFlowPanel(JComponent component, int alignment) { JPanel panel; panel = new JPanel(); panel.setLayout(new FlowLayout(alignment)); panel.add(component); return panel; } /** * Expand all the nodes in the tree representation of the model */ private void expandAll() { for (int row = 0; row < ontModelTree.getRowCount(); ++row) { ontModelTree.expandRow(row); } } /** * Collapse all the nodes in the tree representation of the model */ private void collapseAll() { for (int row = 0; row < ontModelTree.getRowCount(); ++row) { ontModelTree.collapseRow(row); } } /** * Set the status message to the supplied text. * * @param message * The message to place in the status field */ private void setStatus(String message) { status.setText(message); } /** * Sets the mouse pointer. If the supplied parameter is true then the wait * cursor (usually an hourglass) is displayed. otherwise the system default * cursor is displayed. * * @param wait * Whether to display the system default wait cursor */ private void setWaitCursor(boolean wait) { JRootPane rootPane = getRootPane(); Component glassPane = rootPane.getGlassPane(); if (wait) { Cursor cursorWait = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); rootPane.setCursor(cursorWait); glassPane.setCursor(cursorWait); glassPane.setVisible(true); } else { Cursor cursorDefault = Cursor .getPredefinedCursor(Cursor.DEFAULT_CURSOR); glassPane.setVisible(false); glassPane.setCursor(cursorDefault); rootPane.setCursor(cursorDefault); } glassPane.invalidate(); rootPane.validate(); } /** * Run the reasoner or the SPARQL query */ public void run() { enableControls(false); try { setStatus("Running..."); setWaitCursor(true); if (isRunReasoner) { reasonerExecution(); } else { sparqlExecution(); } } catch (Throwable throwable) { setStatus("Error: " + throwable.getClass().getName() + ": " + throwable.getMessage()); LOGGER.error("Failed during execution", throwable); JOptionPane.showMessageDialog(this, "Error: " + throwable.getClass().getName() + "\n\n" + throwable.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } finally { setWaitCursor(false); enableControls(true); } } /** * Setup to run the reasoner and start a thread */ private void runReasoner() { isRunReasoner = true; new Thread(this).start(); } /** * Setup to run the SPARQL query and start a thread */ private void runSPARQL() { isRunReasoner = false; new Thread(this).start(); } /** * Execute the steps to run the reasoner */ private void reasonerExecution() { setStatus("Running reasoner"); results.setText(""); loadModel(); outputResults(); setStatus("Ready"); } /** * Execute the steps to run the SPARQL query */ private void sparqlExecution() { int numResults; setStatus("Running SPARQL"); setWaitCursor(true); // sparqlResults.setText(""); numResults = callSparqlEngine(); setStatus("Number of Results: " + numResults); } /** * Use the SPARQL engine and report the results * * @return The number of resulting rows */ private int callSparqlEngine() { QueryExecution qe; String serviceUrl; ResultSet resultSet; // Get the query String queryString = sparqlInput.getText().trim(); // Create a Query instance Query query = QueryFactory.create(queryString); serviceUrl = ((String) sparqlServiceUrl.getSelectedItem()).trim(); // Execute the query and obtain results if (sparqlServiceUrl.getSelectedIndex() == 0 || serviceUrl.length() == 0) { qe = QueryExecutionFactory.create(query, ontModel); } else { qe = QueryExecutionFactory.sparqlService(serviceUrl, query); } resultSet = qe.execSelect(); SparqlTableModel tableModel = (SparqlTableModel) sparqlResultsTable .getModel(); tableModel.setupModel(resultSet); // Important - free up resources used running the query qe.close(); return tableModel.getRowCount(); } /** * Get the set of defined ontology file formats that the program can load as * a CSV list String * * @return The known ontology file formats as a CSV list */ public final static String getFormatsAsCSV() { return getArrayAsCSV(FORMATS); } /** * Get the set of reasoning levels that the program will use as a CSV list * String * * @return The known reasoning levels as a CSV list */ public final static String getReasoningLevelsAsCSV() { return getArrayAsCSV(REASONING_LEVELS); } /** * Create a CSV list from a String array * * @param array * An array * @return The array values in a CSV list */ public final static String getArrayAsCSV(String[] array) { StringBuffer csv; csv = new StringBuffer(); for (String value : array) { if (csv.length() > 0) { csv.append(", "); } csv.append(value); } return csv.toString(); } /** * Set the RDF file , where the ontology is located * * @param pRdfFile * The file containing the ontology */ public void setRdfFile(File pRdfFile) { rdfFile = pRdfFile; setTitle(); } /** * Set the window title */ private void setTitle() { String title; title = "Semantic UI"; if (assertionLanguage != null) { title += " - " + assertionLanguage; } if (rdfFile != null) { title += " - " + rdfFile.getName(); } setTitle(title); } /** * Convert the ontology into a set of Strings representing the triples * * @return A Map containing Lists that relate subjects to objects and * predicates */ private void outputResults() { StringWriter writer; Model tempModel; LOGGER.debug("ontModelNoInference [" + ontModelNoInference + "]"); LOGGER.debug("ontModel [" + ontModel + "]"); tempModel = ontModel.difference(ontModelNoInference); writer = new StringWriter(); tempModel.write(writer, assertionLanguage); results.setText(writer.toString()); } /** * Create a model with a reasoner set based on the chosen reasoning level. * * @param reasoningLevel * The reasoning level for this model * * @return The created ontology model */ private OntModel createModel(String reasoningLevelName) { OntModel model; int reasoningLevelIndex; model = null; LOGGER.debug("Create model using reasoning level: " + reasoningLevelName); reasoningLevelIndex = getReasoningLevelIndex(reasoningLevelName); LOGGER.debug("Reasoning level index: " + reasoningLevelIndex); if (reasoningLevelIndex == 0) { // None model = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM); } else if (reasoningLevelIndex == 1) { // RDFS model = ModelFactory .createOntologyModel(OntModelSpec.OWL_DL_MEM_RDFS_INF); } else if (reasoningLevelIndex == 2) { // OWL Reasoner reasoner = PelletReasonerFactory.theInstance().create(); Model infModel = ModelFactory.createInfModel(reasoner, ModelFactory .createDefaultModel()); model = ModelFactory.createOntologyModel(OntModelSpec.OWL_DL_MEM, infModel); } return model; } /** * Obtain a text file containing a set of assertions. Load the assertions * into the ontology model. */ private void loadModel() { String modelFormat; modelFormat = null; if (language.getSelectedIndex() == 0) { for (String format : FORMATS) { try { tryFormat(format); modelFormat = format; break; } catch (Throwable throwable) { LOGGER.debug("Error processing assertions as format: " + format, throwable); } } } else { try { tryFormat(language.getSelectedItem().toString()); modelFormat = language.getSelectedItem().toString(); } catch (Throwable throwable) { LOGGER.error("Error processing assertions as format: " + language.getSelectedItem().toString(), throwable); } } if (modelFormat == null) { if (language.getSelectedIndex() == 0) { throw new IllegalStateException( "The assertions cannot be loaded using known languages.\nTried: " + getFormatsAsCSV()); } else { throw new IllegalStateException( "The assertions cannot be loaded using the input format: " + language.getSelectedItem().toString()); } } else { LOGGER.info("Loaded assertions" + " using format: " + modelFormat); } assertionLanguage = modelFormat; setTitle(); } /** * Attempt to load a set of assertions with the supplied format (e.g. N3, * RDF/XML, etc) * * @param format * The format to use, must be a value in the array FORMATS * @throws UnsupportedEncodingException * If the assertions cannot be loaded */ private void tryFormat(String format) throws UnsupportedEncodingException { InputStream inputStream = null; try { inputStream = new ByteArrayInputStream(assertions.getText() .getBytes("UTF-8")); ontModelNoInference = createModel("NONE"); ontModelNoInference.read(inputStream, null, format.toUpperCase()); inputStream = new ByteArrayInputStream(assertions.getText() .getBytes("UTF-8")); ontModel = createModel(reasoningLevel.getSelectedItem().toString()); ontModel.read(inputStream, null, format.toUpperCase()); createTreeFromModel(); } finally { try { inputStream.close(); } catch (Throwable throwable) { LOGGER.error("Error closing input file", throwable); } } } /** * Build a tree representation of the semantic model * * TODO aggregate items from duplicate nodes */ private void createTreeFromModel() { DefaultMutableTreeNode treeTopNode; DefaultMutableTreeNode classesNode; DefaultMutableTreeNode oneClassNode; DefaultMutableTreeNode oneIndividualNode; DefaultMutableTreeNode onePropertyNode; OntClass ontClass; Individual individual; Statement statement; Property property; RDFNode rdfNode; ExtendedIterator classesIterator; ExtendedIterator individualsIterator; StmtIterator stmtIterator; treeTopNode = new DefaultMutableTreeNode("Model"); // Classes classesNode = new DefaultMutableTreeNode("Classes"); treeTopNode.add(classesNode); classesIterator = ontModel.listClasses(); while (classesIterator.hasNext()) { ontClass = classesIterator.next(); if (ontClass.isAnon()) { oneClassNode = new DefaultMutableTreeNode("Anonymous Class (" + ontClass.getId().getLabelString() + ")"); } else { oneClassNode = new DefaultMutableTreeNode(ontClass .getLocalName() + " (" + ontClass.getURI() + ")"); } classesNode.add(oneClassNode); // Individuals individualsIterator = ontModel.listIndividuals(ontClass); while (individualsIterator.hasNext()) { individual = individualsIterator.next(); if (individual.isAnon()) { oneIndividualNode = new DefaultMutableTreeNode( "Anonymous Individual (" + individual.getId().getLabelString() + ")"); } else { oneIndividualNode = new DefaultMutableTreeNode(individual .getLocalName() + " (" + individual.getURI() + ")"); } oneClassNode.add(oneIndividualNode); // Properties (predicates) and Objects stmtIterator = individual.listProperties(); while (stmtIterator.hasNext()) { statement = stmtIterator.next(); property = statement.getPredicate(); rdfNode = statement.getObject(); if (property.isAnon()) { onePropertyNode = new DefaultMutableTreeNode( "Anonymous Property (" + property.getId().getLabelString() + ")"); } else { onePropertyNode = new DefaultMutableTreeNode(property .getLocalName() + " (" + property.getURI() + ")"); } oneIndividualNode.add(onePropertyNode); if (rdfNode.isLiteral()) { onePropertyNode.add(new DefaultMutableTreeNode( "Literal (" + statement.getString() + ")")); } else { onePropertyNode.add(new DefaultMutableTreeNode( statement.getResource().getLocalName() + " (" + statement.getResource().getURI() + ")")); } } } } ontModelTree.setModel(new DefaultTreeModel(treeTopNode)); } /** * Get the index position of the supplied reasoning level label * * @param reasonerName * A reasoning level label * * @return The index position of the reasoning level. Will be equal to the * constant UNKNOWN if the value cannot be found in the collection * of known reasoning levels */ public final static int getReasoningLevelIndex(String reasonerName) { return getIndexValue(REASONING_LEVELS, reasonerName); } /** * Find a String value within an array of Strings. Return the index position * where the value was found. * * @param array * An array of string to search * @param name * The value to find in the array * * @return The position where the value was found in the array. Will be * equal to the constant UNKNOWN if the value cannot be found in the * collection of known reasoning levels */ public final static int getIndexValue(String[] array, String name) { Integer indexValue; indexValue = null; for (int index = 0; index < array.length && indexValue == null; ++index) { if (array[index].toUpperCase().equals(name.toUpperCase())) { indexValue = index; } } return indexValue == null ? UNKNOWN : indexValue; } /** * Allow the user to select a file, which is expected to be an ontology, and * then load the file. */ private void openOntologyFile() { JFileChooser fileChooser; File chosenFile; fileChooser = new JFileChooser(new File(".")); fileChooser.showOpenDialog(this); chosenFile = fileChooser.getSelectedFile(); if (chosenFile != null) { loadOntologyFile(chosenFile); } else { JOptionPane.showMessageDialog(this, "No file to load", "No File Selected", JOptionPane.WARNING_MESSAGE); } enableControls(true); } /** * Load the provided file as an ontology replacing any assertions currently * in the assertions text area. * * @param inputFile * The file to load (should be an ontology) */ private void loadOntologyFile(File inputFile) { BufferedReader reader; String data; StringBuffer allData; reader = null; allData = new StringBuffer(); try { reader = new BufferedReader(new FileReader(inputFile)); while ((data = reader.readLine()) != null) { allData.append(data); allData.append('\n'); } assertions.setText(allData.toString()); assertions.moveCaretPosition(0); setStatus("Loaded file: " + inputFile.getName()); setRdfFile(inputFile); } catch (IOException ioExc) { setStatus("Unable to load file: " + inputFile.getName()); JOptionPane.showMessageDialog(this, "Error: Unable to read file\n\n" + inputFile.getAbsolutePath() + "\n\n" + ioExc.getMessage(), "Error Reading File", JOptionPane.ERROR_MESSAGE); LOGGER.error("Unable to load the file: " + inputFile.getAbsolutePath(), ioExc); } finally { if (reader != null) { try { reader.close(); } catch (Throwable throwable) { LOGGER.error("Unable to close input file", throwable); } } } } /** * Save the text from the assertions text area to a file Note that this is * not the model and it may not be in an legal syntax for RDF triples. It is * simply storing what the user has placed in the text area. * * @see writeOntologyModel */ private void saveAssertionsToFile() { FileWriter out; JFileChooser fileChooser; File destinationFile; boolean okayToWrite; int choice; out = null; fileChooser = new JFileChooser(); if (rdfFile != null) { fileChooser.setSelectedFile(rdfFile); } else { fileChooser.setSelectedFile(new File(".")); } choice = fileChooser.showSaveDialog(this); destinationFile = fileChooser.getSelectedFile(); // Did not click save, did not select a file or chose a directory // So do not write anything if (choice != JFileChooser.APPROVE_OPTION || destinationFile == null || (destinationFile.exists() && !destinationFile.isFile())) { return; // EARLY EXIT! } okayToWrite = !destinationFile.exists(); if (!okayToWrite) { int verifyOverwrite; verifyOverwrite = JOptionPane.showConfirmDialog(this, "The file exists: " + destinationFile.getName() + "\n\nOkay to overwrite?", "Overwrite File?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); okayToWrite = verifyOverwrite == JOptionPane.YES_OPTION; } if (okayToWrite) { LOGGER.info("Write assertions to file, " + destinationFile + ", in format: " + assertionLanguage); try { out = new FileWriter(destinationFile, false); out.write(assertions.getText()); } catch (IOException ioExc) { LOGGER.error("Unable to write to file: " + destinationFile, ioExc); throw new RuntimeException("Unable to write output file (" + destinationFile + ")", ioExc); } finally { if (out != null) { try { out.close(); } catch (Throwable throwable) { LOGGER.error("Failed to close output file: " + destinationFile, throwable); throw new RuntimeException( "Failed to close output file", throwable); } } } } } /** * Get the name of the selected output format * * @return the name of the output format (e.g. N3, Turtle, RDF/XML) */ private String getSelectedOutputLanguage() { int selectedItem; selectedItem = 0; for (int index = 1; selectedItem == 0 && index < setupOutputAssertionLanguage.length; ++index) { if (setupOutputAssertionLanguage[index].isSelected()) { selectedItem = index; } } return selectedItem == 0 ? assertionLanguage : FORMATS[selectedItem - 1]; } /** * Insert standard prefixes in the assertions text area. * * The user must choose the format (syntax) to use. */ private void insertPrefixes() { String whichFormat; int formatIndex; String[] formatsToChoose; List formatsAvail; StringBuffer prefixesToAdd; JTextArea areaToUpdate; formatsAvail = new ArrayList(); for (int format = 0; format < FORMATS.length; ++format) { if (STANDARD_PREFIXES[format].length > 0) { formatsAvail.add(FORMATS[format]); } } // SPARQL is a special case - not an RDF serialization formatsAvail.add("SPARQL (tab)"); formatsToChoose = new String[formatsAvail.size()]; for (int format = 0; format < formatsAvail.size(); ++format) { formatsToChoose[format] = formatsAvail.get(format); } whichFormat = (String) JOptionPane.showInputDialog(this, "Choose the format for the prefixes", "Choose Format", JOptionPane.QUESTION_MESSAGE, null, formatsToChoose, null); LOGGER.debug("Chosen format for prefixes: " + whichFormat); if (whichFormat != null) { formatIndex = getIndexValue(FORMATS, whichFormat); // SPARQL - special case - not an RDF serialization format if (formatIndex == UNKNOWN) { formatIndex = STANDARD_PREFIXES.length - 1; areaToUpdate = sparqlInput; } else { areaToUpdate = assertions; } LOGGER.debug("Chosen format index for prefixes: " + formatIndex); if (formatIndex >= 0) { String currentData; currentData = areaToUpdate.getText(); prefixesToAdd = new StringBuffer(); for (int prefix = 0; prefix < STANDARD_PREFIXES[formatIndex].length; ++prefix) { prefixesToAdd .append(STANDARD_PREFIXES[formatIndex][prefix]); prefixesToAdd.append('\n'); } areaToUpdate.setText(prefixesToAdd.toString() + currentData); } } } /** * Writes the triples to a data file. * * This writes the current model to the file. The current model is the last * model successfully run through the reasoner. The output will be in the * same language (N3, Turtle, RDF/XML) as the assertions input unless a * specific language was set for output. * * @see saveAssertionsToFile */ private void writeOntologyModel() { FileWriter out; JFileChooser fileChooser; File destinationFile; boolean okayToWrite; int choice; out = null; fileChooser = new JFileChooser(); if (rdfFile != null) { fileChooser.setSelectedFile(rdfFile); } else { fileChooser.setSelectedFile(new File(".")); } choice = fileChooser.showSaveDialog(this); destinationFile = fileChooser.getSelectedFile(); // Did not click save, did not select a file or chose a directory // So do not write anything if (choice != JFileChooser.APPROVE_OPTION || destinationFile == null || (destinationFile.exists() && !destinationFile.isFile())) { return; // EARLY EXIT! } okayToWrite = !destinationFile.exists(); if (!okayToWrite) { int verifyOverwrite; verifyOverwrite = JOptionPane.showConfirmDialog(this, "The file exists: " + destinationFile.getName() + "\n\nOkay to overwrite?", "Overwrite File?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); okayToWrite = verifyOverwrite == JOptionPane.YES_OPTION; } if (okayToWrite) { LOGGER.info("Write model to file, " + destinationFile + ", in format: " + assertionLanguage); try { out = new FileWriter(destinationFile, false); if (setupOutputModelTypeAssertionsAndInferences.isSelected()) { LOGGER .info("Writing complete model (assertions and inferences)"); ontModel.write(out, getSelectedOutputLanguage() .toUpperCase()); } else { LOGGER.info("Writing assertions only"); ontModelNoInference.write(out, getSelectedOutputLanguage() .toUpperCase()); } } catch (IOException ioExc) { LOGGER.error("Unable to write to file: " + destinationFile, ioExc); throw new RuntimeException("Unable to write output file (" + destinationFile + ")", ioExc); } finally { if (out != null) { try { out.close(); } catch (Throwable throwable) { LOGGER.error("Failed to close output file: " + destinationFile, throwable); throw new RuntimeException( "Failed to close output file", throwable); } } } } } /** * Presents a classic "About" box to the user displaying the current version * of the application. */ private void about() { StringBuffer message; message = new StringBuffer(); message.append("Semantic UI\n\nVersion:"); message.append(VERSION); message.append("\n\nDavid Read\n\n"); message.append("Jena Version: "); message.append(getJenaVersion()); message.append("\n"); message.append("Pellet Version: "); message.append(getPelletVersion()); JOptionPane.showMessageDialog(this, message.toString(), "About Semantic UI", JOptionPane.INFORMATION_MESSAGE); } /** * Get the version of the loaded Jena library * * @return The version of the Jena library */ private String getJenaVersion() { return ARQ.VERSION; } /** * Get the version of the loaded Pellet library * * @return The version of the Pellet library */ private String getPelletVersion() { return VersionInfo.getInstance().getVersionString() + " (" + VersionInfo.getInstance().getReleaseDate() + ")"; } /** * End the application */ private void closeApplication() { setVisible(false); LOGGER.info("Shutdown"); System.exit(0); } @Override public void windowActivated(WindowEvent arg0) { } @Override public void windowClosed(WindowEvent arg0) { } @Override public void windowClosing(WindowEvent arg0) { closeApplication(); } @Override public void windowDeactivated(WindowEvent arg0) { } @Override public void windowDeiconified(WindowEvent arg0) { } @Override public void windowIconified(WindowEvent arg0) { } @Override public void windowOpened(WindowEvent arg0) { } /** * Executes the reasoner in response to action from any widget being * monitored */ private class ReasonerListener implements ActionListener { public void actionPerformed(ActionEvent e) { runReasoner(); } } /** * Executes the SPARQL query in response to action from any widget being * monitored */ private class SparqlListener implements ActionListener { public void actionPerformed(ActionEvent e) { runSPARQL(); } } /** * Opens an ontology file */ private class FileOpenListener implements ActionListener { public void actionPerformed(ActionEvent e) { openOntologyFile(); } } /** * Exits the application */ private class EndApplicationListener implements ActionListener { public void actionPerformed(ActionEvent e) { closeApplication(); } } /** * Evaluates control status */ private class SparqlModelChoiceListener implements ActionListener { public void actionPerformed(ActionEvent e) { enableControls(true); } } /** * Inserts standard prefixes */ private class InsertPrefixesListener implements ActionListener { public void actionPerformed(ActionEvent e) { insertPrefixes(); } } /** * Writes assertions to a file */ private class FileSaveListener implements ActionListener { public void actionPerformed(ActionEvent e) { saveAssertionsToFile(); } } /** * Expands the nodes in the tree representation of the model */ private class ExpandTreeListener implements ActionListener { public void actionPerformed(ActionEvent e) { expandAll(); } } /** * Collapses the nodes in the tree representation of the model */ private class CollapseTreeListener implements ActionListener { public void actionPerformed(ActionEvent e) { collapseAll(); } } /** * Allows the user to change the display font */ private class FontSetupListener implements ActionListener { public void actionPerformed(ActionEvent e) { configureFont(); } } /** * Writes the current model to an ontology file */ private class ModelSerializerListener implements ActionListener { public void actionPerformed(ActionEvent e) { writeOntologyModel(); } } /** * Displays About dialog */ private class AboutListener implements ActionListener { public void actionPerformed(ActionEvent e) { about(); } } /** * Updates state of controls based on changes to text widgets being * monitored */ private class UserInputListener implements KeyListener { @Override public void keyPressed(KeyEvent arg0) { } @Override public void keyReleased(KeyEvent arg0) { enableControls(true); } @Override public void keyTyped(KeyEvent arg0) { enableControls(true); } } /** * The execution point for the program. Creates an instance of the * SemanticUI class. * * @param args * The array of input arguments, not used yet */ public static void main(String[] args) { org.apache.log4j.PropertyConfigurator.configure("log4j.properties"); new SemanticUI(); } } /** * A basic table model for reporting the SPARQL results * * @author David Read */ @SuppressWarnings("serial") class SparqlTableModel extends AbstractTableModel { /** * Logger Instance */ private static Logger LOGGER = Logger.getLogger(SparqlTableModel.class); /** * The result rows */ private List> rows = new ArrayList>(); /** * The column labels */ private List columnLabels = new ArrayList(); /** * Creates a new table model populating the model with the supplied results * * @param results * A Jena query result set */ public SparqlTableModel(ResultSet results) { setupModel(results); } /** * Creates a new table model that contains no data */ public SparqlTableModel() { } /** * Replaces the current data in the table model with the supplied data. * * @param results * A Jena query result set */ public void setupModel(ResultSet results) { rows.clear(); columnLabels.clear(); while (results.hasNext()) { QuerySolution solution = results.next(); if (columnLabels.size() == 0) { Iterator names; names = solution.varNames(); while (names.hasNext()) { columnLabels.add(names.next().toString()); LOGGER.debug("Added column label: " + columnLabels.get(columnLabels.size() - 1)); } } List row = new ArrayList(); for (String var : columnLabels) { if (solution.get(var).isLiteral()) { row.add("Lit: " + solution.getLiteral(var)); } else { row.add(solution.getResource(var).toString()); } } rows.add(row); LOGGER.debug("Added row with col count: " + row.size()); } LOGGER.debug("Total rows in results: " + rows.size()); fireTableStructureChanged(); } @Override public int getColumnCount() { // TODO Auto-generated method stub return columnLabels.size(); } @Override public int getRowCount() { // TODO Auto-generated method stub return rows.size(); } @Override public Object getValueAt(int arg0, int arg1) { // TODO Auto-generated method stub return rows.get(arg0).get(arg1); } @Override public String getColumnName(int col) { return columnLabels.get(col); } } /** * FontChooser * * From: http://examples.oreilly.com/jswing2/code/ch12/FontChooser.java * * A font chooser that allows users to pick a font by name, size, style, and * color. The color selection is provided by a JColorChooser pane. This dialog * builds an AttributeSet suitable for use with JTextPane. * * DSR: Minor alteration to make all attributes private */ @SuppressWarnings("serial") class FontChooser extends JDialog implements ActionListener { private JColorChooser colorChooser; private JComboBox fontName; private JCheckBox fontBold, fontItalic; private JTextField fontSize; private JLabel previewLabel; private SimpleAttributeSet attributes; private Font newFont; private Color newColor; public FontChooser(Frame parent) { super(parent, "Font Chooser", true); setSize(450, 450); attributes = new SimpleAttributeSet(); // Make sure that any way the user cancels the window does the right // thing addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { closeAndCancel(); } }); // Start the long process of setting up our interface Container c = getContentPane(); JPanel fontPanel = new JPanel(); fontName = new JComboBox(new String[] { "TimesRoman", "Helvetica", "Courier" }); fontName.setSelectedIndex(1); fontName.addActionListener(this); fontSize = new JTextField("12", 4); fontSize.setHorizontalAlignment(SwingConstants.RIGHT); fontSize.addActionListener(this); fontBold = new JCheckBox("Bold"); fontBold.setSelected(true); fontBold.addActionListener(this); fontItalic = new JCheckBox("Italic"); fontItalic.addActionListener(this); fontPanel.add(fontName); fontPanel.add(new JLabel(" Size: ")); fontPanel.add(fontSize); fontPanel.add(fontBold); fontPanel.add(fontItalic); c.add(fontPanel, BorderLayout.NORTH); // Set up the color chooser panel and attach a change listener so that // color // updates get reflected in our preview label. colorChooser = new JColorChooser(Color.black); colorChooser.getSelectionModel().addChangeListener( new ChangeListener() { public void stateChanged(ChangeEvent e) { updatePreviewColor(); } }); c.add(colorChooser, BorderLayout.CENTER); JPanel previewPanel = new JPanel(new BorderLayout()); previewLabel = new JLabel("Here's a sample of this font."); previewLabel.setForeground(colorChooser.getColor()); previewPanel.add(previewLabel, BorderLayout.CENTER); // Add in the Ok and Cancel buttons for our dialog box JButton okButton = new JButton("Ok"); okButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { closeAndSave(); } }); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { closeAndCancel(); } }); JPanel controlPanel = new JPanel(); controlPanel.add(okButton); controlPanel.add(cancelButton); previewPanel.add(controlPanel, BorderLayout.SOUTH); // Give the preview label room to grow. previewPanel.setMinimumSize(new Dimension(100, 100)); previewPanel.setPreferredSize(new Dimension(100, 100)); c.add(previewPanel, BorderLayout.SOUTH); } // Ok, something in the font changed, so figure that out and make a // new font for the preview label public void actionPerformed(ActionEvent ae) { // Check the name of the font if (!StyleConstants.getFontFamily(attributes).equals( fontName.getSelectedItem())) { StyleConstants.setFontFamily(attributes, (String) fontName .getSelectedItem()); } // Check the font size (no error checking yet) if (StyleConstants.getFontSize(attributes) != Integer.parseInt(fontSize .getText())) { StyleConstants.setFontSize(attributes, Integer.parseInt(fontSize .getText())); } // Check to see if the font should be bold if (StyleConstants.isBold(attributes) != fontBold.isSelected()) { StyleConstants.setBold(attributes, fontBold.isSelected()); } // Check to see if the font should be italic if (StyleConstants.isItalic(attributes) != fontItalic.isSelected()) { StyleConstants.setItalic(attributes, fontItalic.isSelected()); } // and update our preview label updatePreviewFont(); } // Get the appropriate font from our attributes object and update // the preview label protected void updatePreviewFont() { String name = StyleConstants.getFontFamily(attributes); boolean bold = StyleConstants.isBold(attributes); boolean ital = StyleConstants.isItalic(attributes); int size = StyleConstants.getFontSize(attributes); // Bold and italic don’t work properly in beta 4. Font f = new Font(name, (bold ? Font.BOLD : 0) + (ital ? Font.ITALIC : 0), size); previewLabel.setFont(f); } // Get the appropriate color from our chooser and update previewLabel protected void updatePreviewColor() { previewLabel.setForeground(colorChooser.getColor()); // Manually force the label to repaint previewLabel.repaint(); } public Font getNewFont() { return newFont; } public Color getNewColor() { return newColor; } public AttributeSet getAttributes() { return attributes; } public void closeAndSave() { // Save font & color information newFont = previewLabel.getFont(); newColor = previewLabel.getForeground(); // Close the window setVisible(false); } public void closeAndCancel() { // Erase any font information and then close the window newFont = null; newColor = null; setVisible(false); } }