package com.github.lindenb.jvarkit.tools.misc;
import htsjdk.samtools.util.CloserUtil;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
import com.github.lindenb.jvarkit.util.jcommander.Launcher;
import com.github.lindenb.jvarkit.util.jcommander.Program;
import com.github.lindenb.jvarkit.util.log.Logger;
import com.github.lindenb.jvarkit.util.ns.RDF;
@SuppressWarnings("serial")
@Program(name="buildwpontology",description="Build a simple RDFS/XML ontology from the Wikipedia Categories")
public class BuildWikipediaOntology extends Launcher
{
final static String RDFS="http://www.w3.org/2000/01/rdf-schema#";
private static final Logger LOG = Logger.build(BuildWikipediaOntology.class).make();
private static long ID_GENERATOR=0L;
private class Frame extends JFrame
{
private JDesktopPane desktopPane;
private class IFrame extends JInternalFrame
{
private JTree jtree;
private File saveAs;
private class CatNode extends DefaultMutableTreeNode
{
private long id=(++ID_GENERATOR);
private String catName;
CatNode(String catName)
{
super(catName,true);
this.catName=catName;
}
boolean isMainNode()
{
return getIdByTerm(this.catName)==this.id;
}
private void delete()
{
CatNode parent= (CatNode)CatNode.this.getParent();
if(parent==null) return;
CatNode.this.removeFromParent();
DefaultTreeModel model=(DefaultTreeModel)jtree.getModel();
model.nodeStructureChanged(parent);
}
private void expand()
{
Set<String> terms=getSubCategories(CatNode.this.catName);
for(int i=0;i< CatNode.this.getChildCount();++i)
{
CatNode c=CatNode.class.cast( CatNode.this.getChildAt(i));
terms.remove(c.catName);
}
for(String term:terms)
{
CatNode newnode=new CatNode(term);
CatNode.this.add(newnode);
}
DefaultTreeModel model=(DefaultTreeModel)jtree.getModel();
model.nodeStructureChanged(CatNode.this);
}
@Override
public String toString()
{
String s=this.catName;
int colon=s.indexOf(':');
if(colon!=-1) s=s.substring(colon+1);
if(!isMainNode()) s+="*";
return s;
}
}
public IFrame(String name)
{
super(name,true,true,true,true);
this.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
JPanel contentPanel=new JPanel(new BorderLayout(5,5));
this.setContentPane(contentPanel);
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPanel.add(new JScrollPane(this.jtree=new JTree(new DefaultTreeModel(new CatNode(name)))));
this.addInternalFrameListener(new InternalFrameAdapter()
{
@Override
public void internalFrameOpened(InternalFrameEvent e) {
removeInternalFrameListener(this);
CatNode.class.cast(jtree.getModel().getRoot()).expand();
}
});
JToolBar toolBar=new JToolBar();
contentPanel.add(toolBar,BorderLayout.NORTH);
AbstractAction action=new AbstractAction("Expand")
{
@Override
public void actionPerformed(ActionEvent e) {
TreePath selPaths[]=jtree.getSelectionPaths();
if(selPaths==null || selPaths.length==0)return;
for(TreePath selPath:selPaths)
{
CatNode node=(CatNode)selPath.getLastPathComponent();
node.expand();
}
}
};
toolBar.add(new JButton(action));
action.setEnabled(false);
getActionMap().put("action.expand", action);
action=new AbstractAction("Delete")
{
@Override
public void actionPerformed(ActionEvent e) {
TreePath selPaths[]=jtree.getSelectionPaths();
if(selPaths==null || selPaths.length==0)return;
for(TreePath selPath:selPaths)
{
CatNode node=(CatNode)selPath.getLastPathComponent();
node.delete();
}
}
};
toolBar.add(new JButton(action));
action.setEnabled(false);
getActionMap().put("action.delete", action);
jtree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener()
{
@Override
public void valueChanged(TreeSelectionEvent e)
{
TreePath path=jtree.getSelectionPath();
CatNode node=null;
if(path!=null) node=CatNode.class.cast(path.getLastPathComponent());
getActionMap().get("action.expand").setEnabled(node!=null && node.isMainNode());
getActionMap().get("action.delete").setEnabled(node!=null && node.getParent()!=null);
}
});
JMenuBar menuBar=new JMenuBar();
setJMenuBar(menuBar);
JMenu menu=new JMenu("File");
menuBar.add(menu);
menu.add(new AbstractAction("Save As...")
{
@Override
public void actionPerformed(ActionEvent e) {
doMenuSaveAs();
}
});
menu.add(new AbstractAction("Save")
{
@Override
public void actionPerformed(ActionEvent e) {
doMenuSave(saveAs);
}
});
menu.add(new AbstractAction("Close")
{
@Override
public void actionPerformed(ActionEvent e) {
IFrame.this.setVisible(false);
IFrame.this.dispose();
}
});
}
String getURL(String catName)
{
return "http://en.wikipedia.org/wiki/"+catName.replace(' ', '_');
}
private void visit(XMLStreamWriter w,CatNode root) throws XMLStreamException
{
if(root.isMainNode())
{
w.writeStartElement("rdfs", "Class", RDFS);
w.writeAttribute("rdf", RDF.NS, "about", getURL(root.catName));
int colon=root.catName.indexOf(':');
w.writeStartElement("rdfs", "label", RDFS);
w.writeCharacters(colon==-1?root.catName:root.catName.substring(colon+1));
w.writeEndElement();
for(String parent: getParentTerms(root.catName))
{
w.writeEmptyElement("rdfs", "subClassOf", RDFS);
w.writeAttribute("rdf", RDF.NS, "resource",
getURL(parent)
);
}
w.writeEndElement();
w.writeCharacters("\n");
}
for(int i=0;i< root.getChildCount();++i)
{
CatNode c=CatNode.class.cast(root.getChildAt(i));
visit(w,c);
}
}
private void doMenuSave(File f)
{
if(f==null)
{
doMenuSaveAs();
return;
}
PrintWriter pw=null;
XMLStreamWriter w=null;
try
{
pw=new PrintWriter(f,"UTF-8");
XMLOutputFactory xmlfactory= XMLOutputFactory.newInstance();
w= xmlfactory.createXMLStreamWriter(pw);
w.writeStartDocument("UTF-8","1.0");
w.writeStartElement("rdf", "RDF", RDF.NS);
w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdf",RDF.NS);
w.writeAttribute("xmlns", XMLConstants.XML_NS_URI, "rdfs", RDFS);
w.writeCharacters("\n");
visit(w,CatNode.class.cast(jtree.getModel().getRoot()));
w.writeEndElement();
w.writeEndDocument();
this.saveAs=f;
}
catch(Exception err)
{
JOptionPane.showMessageDialog(this, "Error"+err.getMessage());
}
finally
{
CloserUtil.close(w);
CloserUtil.close(pw);
}
}
private void doMenuSaveAs()
{
JFileChooser chooser=new JFileChooser(saveAs==null?null:saveAs.getParentFile());
if(chooser.showSaveDialog(this)!=JFileChooser.APPROVE_OPTION) return;
File f=chooser.getSelectedFile();
if(f.exists() && JOptionPane.showConfirmDialog(this, f.getPath()+" exists. Overwite ?", "Confirm", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null)!=JOptionPane.OK_OPTION)
{
return;
}
doMenuSave(chooser.getSelectedFile());
}
private Set<String> getParentTerms(CatNode root,String term,Set<String> parents)
{
for(int i=0;i< root.getChildCount();++i)
{
CatNode c=CatNode.class.cast(root.getChildAt(i));
if(c.catName.equals(term)) parents.add(root.catName);
getParentTerms(c,term,parents);
}
return parents;
}
Set<String> getParentTerms(String term)
{
if(jtree==null || jtree.getModel()==null) return Collections.emptySet();
return getParentTerms(CatNode.class.cast(jtree.getModel().getRoot()),term,new HashSet<String>());
}
private long getIdByTerm(CatNode root,String term,long curr)
{
if(root.catName.equals(term) && (curr==-1L || curr>root.id))
{
curr=root.id;
}
for(int i=0;i< root.getChildCount();++i)
{
CatNode c=CatNode.class.cast(root.getChildAt(i));
long n=getIdByTerm(c,term,curr);
if(curr==-1 || curr>n ) curr=n;
}
return curr;
}
long getIdByTerm(String term)
{
if(jtree==null || jtree.getModel()==null) return -1;
return getIdByTerm(CatNode.class.cast(jtree.getModel().getRoot()),term,-1L);
}
}
Frame()
{
super(getProgramName());
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.addWindowListener(new WindowAdapter()
{
@Override
public void windowOpened(WindowEvent e) {
doMenuNewOntology();
}
});
this.desktopPane=new JDesktopPane();
JPanel contentPanel=new JPanel(new BorderLayout());
setContentPane(contentPanel);
contentPanel.add(desktopPane,BorderLayout.CENTER);
JMenuBar menuBar=new JMenuBar();
setJMenuBar(menuBar);
JMenu menu=new JMenu("File");
menuBar.add(menu);
menu.add(new AbstractAction("Quit")
{
@Override
public void actionPerformed(ActionEvent e) {
Frame.this.setVisible(false);
Frame.this.dispose();
}
});
menu=new JMenu("Tools");
menuBar.add(menu);
menu.add(new AbstractAction("New Ontology")
{
@Override
public void actionPerformed(ActionEvent e) {
doMenuNewOntology();
}
});
}
private void doMenuNewOntology()
{
try {
String s=JOptionPane.showInputDialog(this, "Select Root Category","Category:Science");
if(s==null) return;
if(!s.startsWith("Category:"))
{
JOptionPane.showMessageDialog(this, "Root should start with 'Category:'");
return;
}
Dimension d=desktopPane.getSize();
IFrame iframe=new IFrame(s);
iframe.setBounds(50, 50, d.width-100, d.height-100);
desktopPane.add(iframe);
iframe.setVisible(true);
}
catch (Exception err)
{
LOG.error(err);
}
}
private Set<String> getSubCategories(String term)
{
Set<String> set=new HashSet<String>();
String cmcontinue=null;
XMLEventReader r=null;
QName title=new QName("title");
QName att_cmcontinue=new QName("cmcontinue");
try
{
XMLInputFactory xif=XMLInputFactory.newFactory();
do
{
String url="http://en.wikipedia.org/w/api.php?action=query&list=categorymembers&cmnamespace=14&cmlimit=100&format=xml&cmtitle="+
URLEncoder.encode(term,"UTF-8")+
(cmcontinue==null?"":"&cmcontinue="+cmcontinue)
;
LOG.info(url);
cmcontinue=null;
r=xif.createXMLEventReader(new StreamSource(url));
while(r.hasNext())
{
XMLEvent evt=r.nextEvent();
if(!evt.isStartElement()) continue;
StartElement E=evt.asStartElement();
Attribute att=null;
if(E.getName().getLocalPart().equals("cm") && (att=E.getAttributeByName(title))!=null)
{
set.add(att.getValue());
}
else if(E.getName().getLocalPart().equals("categorymembers") && (att=E.getAttributeByName(att_cmcontinue))!=null)
{
cmcontinue=att.getValue();
}
}
} while(cmcontinue!=null);
}
catch (Exception err)
{
LOG.error(err);
}
return set;
}
}
private BuildWikipediaOntology()
{
}
@Override
public int doWork(List<String> args)
{
final Frame frame=new Frame();
try
{
Dimension d=Toolkit.getDefaultToolkit().getScreenSize();
frame.setBounds(100, 100, d.width-200, d.height-200);
SwingUtilities.invokeAndWait(new Runnable()
{
@Override
public void run() {
frame.setVisible(true);
}
});
return 0;
}
catch(Exception err)
{
LOG.error(err);
return -1;
}
finally
{
}
}
public static void main(String[] args) {
new BuildWikipediaOntology().instanceMain(args);
}
}