package org.mindswap.swoop.fun;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import org.mindswap.swoop.SwoopFrame;
import org.mindswap.swoop.SwoopModel;
import org.mindswap.swoop.reasoner.PelletReasoner;
import org.mindswap.swoop.reasoner.SwoopToldReasoner;
import org.mindswap.swoop.renderer.entity.ConciseFormatEntityRenderer;
import org.mindswap.swoop.utils.SetUtils;
import org.mindswap.swoop.utils.owlapi.CorrectedRDFRenderer;
import org.mindswap.swoop.utils.ui.SwingWorker;
import org.semanticweb.owl.model.OWLClass;
import org.semanticweb.owl.model.OWLClassAxiom;
import org.semanticweb.owl.model.OWLDataFactory;
import org.semanticweb.owl.model.OWLDescription;
import org.semanticweb.owl.model.OWLDifferentIndividualsAxiom;
import org.semanticweb.owl.model.OWLDisjointClassesAxiom;
import org.semanticweb.owl.model.OWLEntity;
import org.semanticweb.owl.model.OWLEquivalentClassesAxiom;
import org.semanticweb.owl.model.OWLException;
import org.semanticweb.owl.model.OWLIndividual;
import org.semanticweb.owl.model.OWLObject;
import org.semanticweb.owl.model.OWLOntology;
import org.semanticweb.owl.model.OWLSameIndividualsAxiom;
import org.semanticweb.owl.model.change.AddClassAxiom;
import org.semanticweb.owl.model.change.AddEntity;
import org.semanticweb.owl.model.change.AddIndividualAxiom;
import org.semanticweb.owl.model.change.AddIndividualClass;
import org.semanticweb.owl.model.change.ChangeVisitor;
import org.semanticweb.owl.model.helper.OWLBuilder;
import org.semanticweb.owl.model.helper.OntologyHelper;
import org.xngr.browser.editor.XmlEditorPane;
/**
* Created: Jan 09, 2006
* @author Aditya Kalyanpur
*
*/
public class Sudoku extends JFrame implements ActionListener, MouseListener {
// Core Puzzle Parameters:
static String SUDOKU_NS = "http://sudoku.owl";
int dimen = 9; // default
int subdim = (int) Math.sqrt(dimen);
boolean useIndividuals = true; // default - use individuals for grids in OWL ontology
boolean changed = false; //TEMP FIXME
// UI:
Font tahoma = new Font("Tahoma", Font.PLAIN, 11);
JButton solBtn, clearBtn;
JTable table;
OWLOntology gridOnt;
JMenuItem loadMItem, saveMItem, viewMItem, swoopMItem, changeMItem, defaultMItem1, defaultMItem2;
JCheckBoxMenuItem useIndMItem, useClaMItem;
// Swoop-related stuff:
SwoopModel swoopModel;
SwoopFrame swoopHandler;
// Constructor:
public Sudoku(int gridDimensions) {
init(gridDimensions);
}
public Sudoku(SwoopModel model, SwoopFrame handler, int gridDimensions) {
this.swoopModel = model;
this.swoopHandler = handler;
init(gridDimensions);
}
private void init(int gridDimensions) {
this.dimen = gridDimensions;
this.subdim = (int) Math.sqrt(dimen);
this.setupUI();
}
private void setupUI() {
Container content = this.getContentPane();
// grid table
TableModel dataModel = new AbstractTableModel() {
Object[][] tableContents = new Object[dimen+1][dimen+1];
public int getColumnCount() { return dimen; }
public int getRowCount() { return dimen;}
public Object getValueAt(int row, int col) { return tableContents[row][col]; }
public boolean isCellEditable(int row, int col) { return true; }
public void setValueAt(Object val, int row, int col) {
tableContents[row][col] = val;
}
};
table = new JTable(dataModel);
table.setFont(tahoma);
table.addMouseListener(this);
JScrollPane scrollpane = new JScrollPane(table);
// top panel UI
JLabel lbl = new JLabel("Sudoku Puzzle Grid");
lbl.setFont(tahoma);
clearBtn = new JButton("Clear Grid");
clearBtn.setFont(tahoma);
clearBtn.addActionListener(this);
solBtn = new JButton("Solve Puzzle using Pellet");
solBtn.setFont(tahoma);
solBtn.addActionListener(this);
JToolBar btnPanel = new JToolBar();
btnPanel.add(clearBtn);
btnPanel.add(solBtn);
JLabel explLbl = new JLabel(" (Right-click on grid cell to see explanation)");
explLbl.setFont(tahoma);
btnPanel.add(explLbl);
JPanel topPane = new JPanel();
topPane.setLayout(new BorderLayout());
topPane.add(lbl, "North");
topPane.add(scrollpane, "Center");
topPane.add(btnPanel, "South");
content.setLayout(new GridLayout(1,1));
content.add(topPane);
// setup menu bar
JMenuBar mbar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
mbar.add(fileMenu);
loadMItem = new JMenuItem("Load Puzzle (.owl)");
saveMItem = new JMenuItem("Save Puzzle (.owl)");
fileMenu.add(loadMItem);
fileMenu.add(saveMItem);
JMenu gridMenu = new JMenu("Grid");
changeMItem = new JMenuItem("Change Dimensions...");
changeMItem.addActionListener(this);
gridMenu.add(changeMItem);
mbar.add(gridMenu);
JMenu defaultMenu = new JMenu("Samples");
defaultMItem1 = new JMenuItem("Sample 4X4 Puzzle");
defaultMItem1.addActionListener(this);
defaultMItem2 = new JMenuItem("Sample 9X9 Puzzle");
defaultMItem2.addActionListener(this);
defaultMenu.add(defaultMItem1);
defaultMenu.add(defaultMItem2);
mbar.add(defaultMenu);
JMenu owlMenu = new JMenu("OWL");
mbar.add(owlMenu);
viewMItem = new JMenuItem("View Source");
JMenu repMenu = new JMenu("Represent Grid Cells Using...");
useIndMItem = new JCheckBoxMenuItem("Individuals");
useIndMItem.setSelected(true); // default
useIndMItem.addActionListener(this);
useClaMItem = new JCheckBoxMenuItem("Classes");
useClaMItem.setSelected(false); // default
useClaMItem.addActionListener(this);
repMenu.add(useIndMItem);
repMenu.add(useClaMItem);
swoopMItem = new JMenuItem("Transfer OWL Ontology to Swoop");
owlMenu.add(repMenu);
owlMenu.addSeparator();
owlMenu.add(viewMItem);
owlMenu.add(swoopMItem);
loadMItem.addActionListener(this);
saveMItem.addActionListener(this);
viewMItem.addActionListener(this);
swoopMItem.addActionListener(this);
this.setJMenuBar(mbar);
// setup main frame UI
this.setSize(450, 300);
this.setLocation(300, 100);
this.show();
this.setTitle("Sudoku using OWL");
}
public String getRDFXMLGridOnt() {
String rdf = "";
try {
CorrectedRDFRenderer rdfRend = new CorrectedRDFRenderer();
StringWriter st = new StringWriter();
rdfRend.renderOntology(gridOnt, st);
rdf = st.toString();
}
catch (OWLException ex) {
ex.printStackTrace();
}
return rdf;
}
public void saveGridOntology() {
try {
FileWriter fw = new FileWriter(new File("sudoku.owl"));
fw.write(this.getRDFXMLGridOnt());
fw.close();
System.out.println("Saved grid to file: sudoku.owl");
JOptionPane.showMessageDialog(this, "Saved grid to file: sudoku.owl", "Saving Sudoko OWL Ontology", JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void loadGridOntologyToSwoop() {
try {
this.generatePopulatedSudokuOWLGrid();
swoopModel.addOntology(gridOnt);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void setGridValue(String ind1, String ind2) {
try {
Set entSet = new HashSet();
if (this.useIndividuals) {
entSet.add(gridOnt.getIndividual(new URI(SUDOKU_NS+ind1)));
entSet.add(gridOnt.getIndividual(new URI(SUDOKU_NS+ind2)));
}
else {
entSet.add(gridOnt.getClass(new URI(SUDOKU_NS+ind1)));
entSet.add(gridOnt.getClass(new URI(SUDOKU_NS+ind2)));
}
this.makeEqual(entSet);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public void generatePopulatedSudokuOWLGrid() {
// set useIndividuals based on user selection
this.useIndividuals = this.useIndMItem.isSelected();
// reset gridOnt
this.generateEmptySudokuOWLGrid();
// set individual values in gridOnt based on user inputs in table UI
TableModel tm = table.getModel();
for (int row = 1; row<=dimen; row++) {
for (int col = 1; col<=dimen; col++) {
if (tm.getValueAt(row-1, col-1)!=null) {
String valStr = tm.getValueAt(row-1, col-1).toString();
if (!valStr.equals("")) {
valStr = "#"+valStr;
// map (row, col) to (g, i, j)
int g = ((row-1) / subdim) * subdim + 1 + ((int) (col-1) / subdim);
int i = row % subdim;
if (i==0) i = subdim;
int j = col % subdim;
if (j==0) j = subdim;
String cellValue = "";
if (this.useIndividuals) cellValue = "#V"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
else cellValue = "#C"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
// System.out.println("("+row+","+col+") translates to "+cellValue);
this.setGridValue(cellValue, valStr);
}
}
}
}
}
public OWLOntology generateEmptySudokuOWLGrid() {
System.out.println("Generating sudoku grid of dimensions "+dimen+" X "+dimen+", with "+dimen +" sub-grids of dimension "+subdim+" X "+subdim);
gridOnt = null;
try {
// initialize ontology
OWLBuilder builder = new OWLBuilder();
builder.createOntology(new URI(SUDOKU_NS), new URI(SUDOKU_NS));
gridOnt = builder.getOntology();
OWLDataFactory gridDF = gridOnt.getOWLDataFactory();
// setup game grid
// first init grid table array
OWLObject[][][] grid = new OWLObject[dimen+1][subdim+1][subdim+1];
Set gridEntities = new HashSet();
// now create individuals (/classes) for each grid cell
for (int g=1; g<=dimen; g++) {
for (int i=1; i<=subdim; i++) {
for (int j=1; j<=subdim; j++) {
String gridCell = "";
OWLEntity ent = null;
if (this.useIndividuals) {
gridCell = SUDOKU_NS+"#V"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j);
ent = gridDF.getOWLIndividual(new URI(gridCell));
}
else {
gridCell = SUDOKU_NS+"#C"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j);
ent = gridDF.getOWLClass(new URI(gridCell));
}
AddEntity ae = new AddEntity(gridOnt, ent, null);
ae.accept((ChangeVisitor) gridOnt);
//*** add corresponding elements to grid sets ***
grid[g][i][j] = ent;
gridEntities.add(ent);
if (this.useIndividuals) {
// create dummy classes to see results in pellet online demo
OWLClass cla = gridDF.getOWLClass(new URI(SUDOKU_NS+"#C"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j)));
ae = new AddEntity(gridOnt, cla, null);
ae.accept((ChangeVisitor) gridOnt);
AddIndividualClass ai = new AddIndividualClass(gridOnt, (OWLIndividual) ent, cla, null);
ai.accept((ChangeVisitor) gridOnt);
}
else {
// create dummy individuals for each class
OWLIndividual ind = gridDF.getOWLIndividual(new URI(SUDOKU_NS+"#V"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j)));
ae = new AddEntity(gridOnt, ind, null);
ae.accept((ChangeVisitor) gridOnt);
AddIndividualClass ai = new AddIndividualClass(gridOnt, ind, (OWLClass) ent, null);
ai.accept((ChangeVisitor) gridOnt);
}
}
}
}
// create value individuals(/classes)
Set valSet = new HashSet();
for (int i=1; i<=dimen; i++) {
String gridValue = SUDOKU_NS+"#"+String.valueOf(i);
OWLEntity val;
if (this.useIndividuals) val = gridDF.getOWLIndividual(new URI(gridValue));
else val = gridDF.getOWLClass(new URI(gridValue));
valSet.add(val);
AddEntity ae = new AddEntity(gridOnt, val, null);
ae.accept((ChangeVisitor) gridOnt);
}
// make all values different(/disjoint)
this.makeDifferent(valSet);
// make owl:Thing a subclass of oneOf(1,2,..param) (/OR each class equivalent to unionOf(1,2..param)
OWLDescription topConstraint = null;
if (this.useIndividuals) {
topConstraint = gridDF.getOWLEnumeration(valSet);
}
else {
topConstraint = gridDF.getOWLOr(valSet);
}
OWLClassAxiom cax = gridDF.getOWLSubClassAxiom(gridDF.getOWLThing(), topConstraint);
AddClassAxiom ac = new AddClassAxiom(gridOnt, cax, null);
ac.accept((ChangeVisitor) gridOnt);
// enforce constraints of sudoku puzzle
// make all sub-grid values distinct
for (int g=1; g<=dimen; g++) {
Set subGrid = new HashSet();
for (int i=1; i<=subdim; i++) {
for (int j=1; j<=subdim; j++) {
subGrid.add(grid[g][i][j]);
}
}
this.makeDifferent(subGrid);
}
// make all grid-row values distinct
for (int g=1; g<=dimen; g+=subdim) {
for (int row=1; row<=subdim; row++) {
Set gridRow = new HashSet();
for (int sg=g; sg<=(g+subdim-1); sg++) {
for (int col=1; col<=subdim; col++) {
gridRow.add(grid[sg][row][col]);
}
}
this.makeDifferent(gridRow);
}
}
// make all grid-column values distinct
for (int g=1; g<=subdim; g++) {
for (int col=1; col<=subdim; col++) {
Set gridCol = new HashSet();
for (int sg=g; sg<=dimen; sg+=subdim) {
for (int row=1; row<=subdim; row++) {
gridCol.add(grid[sg][row][col]);
}
}
this.makeDifferent(gridCol);
}
}
}
catch (Exception ex) {
ex.printStackTrace();
}
return gridOnt;
}
private void makeDifferent(Set diff) {
try {
OWLDataFactory gridDF = gridOnt.getOWLDataFactory();
if (this.useIndividuals) {
OWLDifferentIndividualsAxiom dif = gridDF.getOWLDifferentIndividualsAxiom(diff);
AddIndividualAxiom ai = new AddIndividualAxiom(gridOnt, dif, null);
ai.accept((ChangeVisitor) gridOnt);
}
else {
OWLDisjointClassesAxiom dis = gridDF.getOWLDisjointClassesAxiom(diff);
AddClassAxiom ai = new AddClassAxiom(gridOnt, dis, null);
ai.accept((ChangeVisitor) gridOnt);
}
}
catch (OWLException ex) {
ex.printStackTrace();
}
}
private void makeEqual(Set equ) {
try {
OWLDataFactory gridDF = gridOnt.getOWLDataFactory();
if (this.useIndividuals) {
OWLSameIndividualsAxiom sam = gridDF.getOWLSameIndividualsAxiom(equ);
AddIndividualAxiom ai = new AddIndividualAxiom(gridOnt, sam, null);
ai.accept((ChangeVisitor) gridOnt);
}
else {
OWLEquivalentClassesAxiom eq = gridDF.getOWLEquivalentClassesAxiom(equ);
AddClassAxiom ai = new AddClassAxiom(gridOnt, eq, null);
ai.accept((ChangeVisitor) gridOnt);
}
}
catch (OWLException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
Sudoku sud = new Sudoku(9);
}
public void actionPerformed(ActionEvent e) {
try {
if (e.getSource() == defaultMItem1) {
// load sample 4X4 puzzle
try {
gridOnt = swoopModel.loadOntology(new URI("http://www.mindswap.org/~aditkal/sudoku4X4.owl"));
this.parseOWLOntToGrid(gridOnt);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
else
if (e.getSource() == defaultMItem2) {
// load sample 9X9 puzzle
try {
gridOnt = swoopModel.loadOntology(new URI("http://www.mindswap.org/~aditkal/sudoku9X9.owl"));
this.parseOWLOntToGrid(gridOnt);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
else
if (e.getSource() == loadMItem) {
this.loadOWLGrid();
}
else
if (e.getSource() == saveMItem) {
this.saveOWLGrid();
}
else
if (e.getSource() == useIndMItem) {
useClaMItem.setSelected(!useIndMItem.isSelected());
}
else
if (e.getSource() == useClaMItem) {
useIndMItem.setSelected(!useClaMItem.isSelected());
}
else
if (e.getSource() == viewMItem) {
viewSource();
}
else
if (e.getSource() == solBtn)
solveSudoku();
else
if (e.getSource() == clearBtn)
clearTable();
else
if (e.getSource() == swoopMItem)
// saveGridOntology();
this.loadGridOntologyToSwoop();
else if (e.getSource() == changeMItem)
changeDimensions();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void clearTable() {
for (int i = 0; i<table.getModel().getRowCount(); i++)
for (int j = 0; j<table.getModel().getRowCount(); j++)
table.setValueAt("", i, j);
table.updateUI();
}
private void viewSource() {
// generate and view source of OWL
this.generatePopulatedSudokuOWLGrid();
JFrame popup = new JFrame("View Source");
Container content = popup.getContentPane();
content.setLayout(new GridLayout(1,1));
XmlEditorPane xmlPane = new XmlEditorPane();
xmlPane.setEditable(false);
xmlPane.setContentType("text/xml");
xmlPane.setText(this.getRDFXMLGridOnt());
xmlPane.setCaretPosition(0);
content.add(new JScrollPane(xmlPane));
popup.setSize(600, 400);
popup.setLocation(200, 200);
popup.show();
}
private void solveSudoku() {
// generate gridOnt OWL ontology based on puzzle UI
this.generatePopulatedSudokuOWLGrid();
// use Pellet to get solution to puzzle
SwingWorker worker = new SwingWorker() {
PelletReasoner pellet = new PelletReasoner();
boolean fail = false;
public Object construct() {
try {
pellet.setOntology(gridOnt);
}
catch (Exception ex) {
fail = true;
if( ex != null )
throw new RuntimeException(ex.getMessage());
else
throw new RuntimeException( "Unexpected error" );
}
return null;
}
public void finished() {
if (fail) {
}
else {
// pellet has got answers
try {
if (pellet.isConsistent()) {
boolean incomplete = false;
for (int g=1; g<=dimen; g++) {
for (int i=1; i<=subdim; i++) {
for (int j=1; j<=subdim; j++) {
OWLEntity ent = null;
if (useIndividuals) ent = gridOnt.getClass(new URI(SUDOKU_NS+"#C"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j)));
else ent = gridOnt.getIndividual(new URI(SUDOKU_NS+"#V"+ String.valueOf(g) + "_" + String.valueOf(i) + String.valueOf(j)));
Set equ = new HashSet();
if (useIndividuals) equ = pellet.allInstancesOf((OWLClass) ent);
else {
// if (!pellet.isConsistent(cla)) {
// JOptionPane.showMessageDialog(null, "Invalid Data: Puzzle Constraints Violated", "Error", JOptionPane.ERROR_MESSAGE);
// return;
// }
equ = SetUtils.union(pellet.allTypesOf((OWLIndividual) ent));
}
String val = "";
for (Iterator iter = equ.iterator(); iter.hasNext();) {
OWLEntity eq = (OWLEntity) iter.next();
if (eq.getURI().toString().indexOf("#V")==-1 && eq.getURI().toString().indexOf("#C")==-1 && eq.getURI().toString().indexOf("#T")==-1) {
val = eq.getURI().toString();
val = val.substring(val.indexOf("#")+1, val.length());
break;
}
}
// translate g,i,j to (row, col)
int row = ((g - 1) / subdim) * subdim + i;
int c = g % subdim;
if (c==0) c = subdim;
int col = (c-1) * subdim + j;
// System.out.println(g+"_"+String.valueOf(i)+String.valueOf(j)+" translates to "+"("+row+","+col+")");
if (table.getValueAt(row-1, col-1) == null || table.getValueAt(row-1, col-1).toString().equals("") && !val.equals("")) changed = true;
table.setValueAt(val, row-1, col-1);
if (val.equals("")) incomplete = true;
table.updateUI();
}
}
}
//FIXME: iteratively solve sudoku if using classes?!
// if (!useIndividuals && incomplete && changed) {
// changed = false;
// generatePopulatedSudokuOWLGrid();
// solveSudoku();
// }
}
else {
JOptionPane.showMessageDialog(null, "Invalid Data: Puzzle Constraints Violated", "Error", JOptionPane.ERROR_MESSAGE);
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
};
worker.start();
}
private void loadOWLGrid() {
try {
// prompt user for file
JFileChooser wrapChooser = new JFileChooser();
// wrapChooser.addChoosableFileFilter();
File openFile = null;
int returnVal = wrapChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
openFile = wrapChooser.getSelectedFile();
} else { // open cancelled
return;
}
// open file
FileInputStream in = new FileInputStream(openFile);
InputStreamReader reader = new InputStreamReader(in);
URI uri = new URI(openFile.getName());
gridOnt = swoopModel.loadOntologyInRDF(reader, uri);
this.parseOWLOntToGrid(gridOnt);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void parseOWLOntToGrid(OWLOntology gridOnt) {
try {
// first get used classes or individuals
Set uris = OntologyHelper.allURIs(gridOnt);
boolean usedInd = false;
for (Iterator iter = uris.iterator(); iter.hasNext();) {
URI u = (URI) iter.next();
if (u.toString().indexOf("#V")>=0) {
usedInd = true;
break;
}
}
this.useIndividuals = usedInd;
this.useIndMItem.setSelected(usedInd);
this.useClaMItem.setSelected(!usedInd);
// second get grid dimensions
int num = 0;
for (int i=10; i>0; i--) {
String val = "#"+String.valueOf((int) Math.pow(i,2));
if (this.useIndividuals && gridOnt.getIndividual(new URI(SUDOKU_NS+val))!=null) {
num = (int) Math.pow(i,2);
break;
}
if (!this.useIndividuals && gridOnt.getClass(new URI(SUDOKU_NS+val))!=null) {
num = (int) Math.pow(i,2);
break;
}
}
if (dimen!=num) {
this.dimen = num;
this.subdim = (int) Math.sqrt(dimen);
this.getContentPane().removeAll();
this.setupUI();
this.repaint();
}
// populate table
SwoopToldReasoner reas = new SwoopToldReasoner();
reas.setOntology(gridOnt);
for (int g=1; g<=dimen; g++) {
for (int i=1; i<=subdim; i++) {
for (int j=1; j<=subdim; j++) {
OWLEntity ent = null;
String val = "";
if (this.useIndividuals) {
String cell = "#V"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
ent = gridOnt.getIndividual(new URI(SUDOKU_NS+cell));
}
else {
String cell = "#C"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
ent = gridOnt.getClass(new URI(SUDOKU_NS+cell));
}
if (ent==null) {
JOptionPane.showMessageDialog(this, "Error: Invalid Grid Dimensions", "Load Error", JOptionPane.ERROR_MESSAGE);
return;
}
else {
// get value
Set equ = new HashSet();
if (this.useIndividuals) equ = reas.getSameAsIndividuals((OWLIndividual) ent);
else equ = reas.equivalentClassesOf((OWLClass) ent);
for (Iterator iter = equ.iterator(); iter.hasNext();) {
OWLEntity eq = (OWLEntity) iter.next();
if (eq.getURI().toString().indexOf("#V")==-1 && eq.getURI().toString().indexOf("#C")==-1 && eq.getURI().toString().indexOf("#T")==-1) {
val = eq.getURI().toString();
val = val.substring(val.indexOf("#")+1, val.length());
break;
}
}
}
// map g,i,j to row,col
int row = ((g - 1) / subdim) * subdim + i;
int c = g % subdim;
if (c==0) c = subdim;
int col = (c-1) * subdim + j;
table.setValueAt(val, row-1, col-1);
}
}
}
table.updateUI();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void saveOWLGrid() {
try {
JFileChooser wrapChooser = new JFileChooser();
File saveFile = null;
int returnVal = wrapChooser.showSaveDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
saveFile = wrapChooser.getSelectedFile();
} else { // save cancelled
return;
}
// prompt user if overwriting
if (saveFile.exists()) {
int result = JOptionPane.showConfirmDialog(this, "Saving File at " + saveFile.getAbsolutePath() + ". Overwrite?", "Save Ontology", JOptionPane.YES_NO_OPTION);
if (result != JOptionPane.YES_OPTION) return;
}
// write ontology file
FileWriter fw = new FileWriter(saveFile);
this.generatePopulatedSudokuOWLGrid();
fw.write(this.getRDFXMLGridOnt());
fw.close();
System.out.println("Saved grid to file: "+saveFile.getName());
JOptionPane.showMessageDialog(this, "Saved grid to file: "+saveFile.getName(), "Saving Sudoko OWL Ontology", JOptionPane.INFORMATION_MESSAGE);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
private void popupExplanation(int row, int col) {
try {
// translate dimensions to to g, i, j
int g = ((row-1) / subdim) * subdim + 1 + ((int) (col-1) / subdim);
int i = row % subdim;
if (i==0) i = subdim;
int j = col % subdim;
if (j==0) j = subdim;
String val = table.getValueAt(row-1, col-1).toString();
if (!val.equals("") && this.useIndividuals) {
// Cg_ij has instance value
// get explanation
ConciseFormatEntityRenderer cfRend = new ConciseFormatEntityRenderer(); // swoopHandler.termDisplay.renderers.get(0);
cfRend.setSwoopModel(swoopModel);
cfRend.visitor = cfRend.createVisitor();
OWLIndividual ind = gridOnt.getIndividual(new URI(SUDOKU_NS+"#"+val));
OWLClass cla = gridOnt.getClass(new URI(SUDOKU_NS+"#C"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j)));
String expl = cfRend.getSudokuExplanation(gridOnt, ind, cla);
String nlExpl = "<html><body><font face=\"Verdana\" size=2>";
nlExpl += "<b>Cell ("+row+","+col+")</b> has value <b>"+val+"</b> because...<br><hr><font face=\"Verdana\" size=2>";
nlExpl += "Each cell must have <b>one of</b> the values [1..."+String.valueOf(dimen)+"] <i>[constraint]</i><br>";
List nlList = new ArrayList();
// parse explanation to add NL
String[] st = expl.split("<br>");
for (int s=0; s<st.length; s++) {
String line = st[s];
String nl = "";
// System.out.println(line);
if (line.indexOf("SameIndividual")>=0) {
String token = "\">";
int pos = line.indexOf(token)+2;
String val1 = line.substring(pos, line.indexOf("<", pos));
int pos2 = line.indexOf(token, pos)+2;
String val2 = line.substring(pos2, line.indexOf("<", pos2));
if (val2.indexOf("V")>=0) {
String temp = val1;
val1 = val2;
val2 = temp;
}
String gs = val1.substring(1,2);
String is = val1.substring(3,4);
String js = val1.substring(4,5);
g = Integer.parseInt(gs);
i = Integer.parseInt(is);
j = Integer.parseInt(js);
row = ((g - 1) / subdim) * subdim + i;
int c = g % subdim;
if (c==0) c = subdim;
col = (c-1) * subdim + j;
nl = "<b>Cell ("+row+","+col+")</b> has value "+val2 + " <i>[given]</i>";
}
else {
// check grid
for (g=1; g<=dimen; g++) {
boolean match = true;
for (i=1; i<=subdim; i++) {
for (j=1; j<=subdim; j++) {
String cell = "V"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
if (line.indexOf(cell)==-1) {
match = false;
break;
}
}
}
if (match) {
nl = "<b>Grid "+String.valueOf(g)+"</b> must have all values different <i>[constraint]</i>";
break;
}
}
if (nl.equals("")) {
// check row
for (g=1; g<=dimen; g+=subdim) {
for (i=1; i<=subdim; i++) {
boolean match = true;
for (int sg=g; sg<=(g+subdim-1); sg++) {
for (j=1; j<=subdim; j++) {
String cell = "V"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
if (line.indexOf(cell)==-1) {
match = false;
break;
}
}
}
if (match) {
row = ((g - 1) / subdim) * subdim + i;
nl = "<b>Row "+row+"</b> must have all values different <i>[constraint]</i>";
break;
}
}
}
}
if (nl.equals("")) {
// check columns
for (g=1; g<=subdim; g++) {
for (j=1; j<=subdim; j++) {
boolean match = true;
for (int sg=g; sg<=dimen; sg+=subdim) {
for (i=1; i<=subdim; i++) {
String cell = "V"+String.valueOf(g)+"_"+String.valueOf(i)+String.valueOf(j);
if (line.indexOf(cell)==-1) {
match = false;
break;
}
}
}
if (match) {
int c = g % subdim;
if (c==0) c = subdim;
col = (c-1) * subdim + j;
nl = "<b>Column "+col+"</b> must have all values different <i>[constraint]</i>";
break;
}
}
}
}
}
// System.out.println("--->"+nl);
if (!nl.equals("")) nlList.add(nl); //nlExpl += nl+"<br>";
}
// post process to *order* NL
while (!nlList.isEmpty()) {
boolean findVal = false;
String valLine = "";
for (Iterator iter = nlList.iterator(); iter.hasNext();) {
String line = (String) iter.next();
if (line.indexOf("has value")>=0) {
findVal = true;
valLine = line;
break;
}
}
if (findVal) {
nlList.remove(valLine);
// add findVal to output
nlExpl += valLine + "<br>";
String rs = valLine.substring(valLine.indexOf("(")+1, valLine.indexOf(","));
String cs = valLine.substring(valLine.indexOf(",")+1, valLine.indexOf(")"));
row = Integer.parseInt(rs);
col = Integer.parseInt(cs);
// add all grid/row/col constraints after this line
for (Iterator iter = new ArrayList(nlList).iterator(); iter.hasNext();) {
String line = (String) iter.next();
boolean match = false;
if (line.indexOf("Grid")>=0) {
int pos = line.indexOf("Grid")+5;
String gs = line.substring(pos, line.indexOf("</b>")).trim();
g = Integer.parseInt(gs);
// check if row,col appear in g
if (g == ((row-1) / subdim) * subdim + 1 + ((int) (col-1) / subdim)) match = true;
}
else if (line.indexOf("Row")>=0) {
int pos = line.indexOf("Row")+4;
String is = line.substring(pos, line.indexOf("</b>")).trim();
i = Integer.parseInt(is);
// check if i matches
if (i==row) match = true;
}
else if (line.indexOf("Column")>=0) {
int pos = line.indexOf("Column")+7;
String js = line.substring(pos, line.indexOf("</b>")).trim();
j = Integer.parseInt(js);
// check if j matches
if (col==j) match = true;
}
if (match) {
nlExpl += "|_" + line + "<br>";
nlList.remove(line);
}
}
}
else {
// flush remaining constraints
for (Iterator iter = nlList.iterator(); iter.hasNext();) {
String line = (String) iter.next();
nlExpl += line + "<br>";
}
nlList.clear();
}
}
nlExpl += "</body></html>";
// display explanation in popup
JFrame popup = new JFrame("Explanation");
Container content = popup.getContentPane();
content.setLayout(new GridLayout(1,1));
JEditorPane htmlPane = new JEditorPane();
htmlPane.setEditable(false);
htmlPane.setContentType("text/html");
htmlPane.setText(nlExpl);
htmlPane.setCaretPosition(0);
content.add(new JScrollPane(htmlPane));
popup.setSize(400, 200);
popup.setLocation(300, 300);
popup.show();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public void mouseClicked(MouseEvent e) {
if (e.getSource() == table) {
if (e.getButton() == MouseEvent.BUTTON3) {
// popup menu on right click
int row = table.rowAtPoint(e.getPoint());
int col = table.columnAtPoint(e.getPoint());
this.popupExplanation(row+1, col+1);
}
}
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
private void changeDimensions() {
String result = JOptionPane.showInputDialog(this, "Specify New Dimensions", "Puzzle Grid", JOptionPane.INFORMATION_MESSAGE);
if (result!=null) {
int num = Integer.parseInt(result);
if (Math.sqrt(num) != ((int) Math.sqrt(num))) {
JOptionPane.showMessageDialog(this, "Error: Invalid Grid Dimensions - Number needs to be a perfect square", "Dimension Error", JOptionPane.ERROR_MESSAGE);
return;
}
this.dimen = num;
this.subdim = (int) Math.sqrt(dimen);
this.getContentPane().removeAll();
this.setupUI();
this.repaint();
}
}
}