package edu.harvard.wcfia.yoshikoder;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import edu.harvard.wcfia.yoshikoder.concordance.Concordance;
import edu.harvard.wcfia.yoshikoder.dictionary.CategoryNode;
import edu.harvard.wcfia.yoshikoder.dictionary.DuplicateException;
import edu.harvard.wcfia.yoshikoder.dictionary.Node;
import edu.harvard.wcfia.yoshikoder.dictionary.PatternNode;
import edu.harvard.wcfia.yoshikoder.dictionary.YKDictionary;
import edu.harvard.wcfia.yoshikoder.document.DocumentList;
import edu.harvard.wcfia.yoshikoder.document.YKDocument;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenCache;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenList;
import edu.harvard.wcfia.yoshikoder.document.tokenizer.TokenizationCache;
import edu.harvard.wcfia.yoshikoder.ui.DictionaryPanel;
import edu.harvard.wcfia.yoshikoder.ui.DocumentPanel;
import edu.harvard.wcfia.yoshikoder.ui.FatalErrorPanel;
import edu.harvard.wcfia.yoshikoder.ui.ListConcordancePanel;
import edu.harvard.wcfia.yoshikoder.util.DialogUtil;
import edu.harvard.wcfia.yoshikoder.util.FileUtil;
import edu.harvard.wcfia.yoshikoder.util.ImportUtil;
import edu.harvard.wcfia.yoshikoder.util.Messages;
public class Yoshikoder extends JFrame {
private static Logger log =
Logger.getLogger("edu.harvard.wcfia.yoshikoder.Yoshikoder");
// model
protected YKProject project;
// components
protected ListConcordancePanel concPanel;
protected DictionaryPanel dictPanel;
protected DocumentPanel docPanel;
// properties
protected Color highlightColor;
protected boolean unsavedChanges;
protected TokenizationCache tokenizationCache;
// actions
// file menu
private Action newProjectAction = new NewProjectAction(this);
private Action openProjectAction = new OpenProjectAction(this);
private Action saveProjectAction = new SaveProjectAction(this);
private Action saveAsProjectAction = new SaveProjectAsAction(this);
//private Action importVBProAction = new ImportVBProAction(this);
private Action importYKAction = new OpenDictionaryAction(this);
private Action exportProjectAction = new ExportProjectAsHtmlAction(this);
// dictionary menu
private Action addCategoryAction = new AddCategoryAction(this);
private Action addPatternAction = new AddPatternAction(this);
private Action removeNodeAction = new RemoveNodeAction(this);
private Action editNodeAction = new EditNodeAction(this);
private Action addWordsToCategoryAction = new AddWordsToCategoryAction(this);
private Action saveAsDictionaryAction = new SaveAsDictionaryAction(this);
private Action exportDictionaryAsHtmlAction = new ExportDictionaryAsHtml(this);
// highlight menu
private Action addHighlightsAction = new AddHighlightsAction(this);
private Action removeHighlightsAction = new RemoveHighlightsAction(this);
private Action setHighlightColorAction = new SetHighlightColorAction(this);
// concordance menu
private Action makeConcordanceAction = new MakeConcordanceAction(this);
private Action openConcordanceAction = new OpenConcordanceAction(this);
private Action saveConcordanceAction = new SaveConcordanceAction(this);
private Action exportConcordanceAsHtmlAction = new ExportConcordanceAsHtmlAction(this);
private Action exportConcordanceAsExcelAction = new ExportConcordanceAsExcelAction(this);
private Action multipleConcordanceFrequencyReportAction = new MultipleConcordanceFrequencyReportAction(this);
// document menu
private Action addDocumentAction = new AddDocumentAction(this);
private Action editDocumentAction = new EditDocumentAction(this);
private Action removeDocumentAction = new RemoveDocumentAction(this);
private Action importDocumentAction = new ImportDocumentAction(this);
private Action exportDocumentAsUTF8Action = new ExportDocumentAsUTF8Action(this);
private Action exportDocumentAsUTF16Action = new ExportDocumentAsUTF16Action(this);
// report menu
private Action dictionaryReportAction = new SingleDocumentDictionaryReportAction(this);
private Action documentReportAction = new SingleDocumentWordFrequencyReportAction(this);
//private Action comparisonReportAction = new DictionaryComparisonReportAction(this);
private Action statisticalComparisonReportAction =
new DictionaryRRDocumentComparisonAction(this);
private Action concordanceReportAction = new ConcordanceFrequencyReportAction(this);
//private Action allDocumentsReportAction =
// new MultipleDocumentFrequencyReportAction(this);
private Action duplicateReportAction = new DuplicateReportAction(this);
private Action unifiedDocumentReport = new UnifiedWordFrequencyReportAction(this);
private Action unifiedDictionaryReport = new UnifiedDictionaryFrequencyReportAction(this);
// help menu
private Action consoleAction = new ShowConsoleAction(this);
private Action licenseAction = new ShowLicenseAction(this);
// may overridden by platform-specific subclasses
protected Action aboutAction;
protected Action helpAction;
protected Action finishAction;
protected Action preferencesAction = new PreferencesAction(this);
private Charset defaultEncoding = Charset.defaultCharset();
private Locale defaultLocale = Locale.getDefault();
private int windowSize = 6;
public Yoshikoder() {
super("Yoshikoder");
Preferences preferences = Preferences.userNodeForPackage(Yoshikoder.class);
String charsetName = preferences.get("default.charset", Charset.defaultCharset().name());
setDefaultEncoding(Charset.forName(charsetName));
String localeName = preferences.get("default.locale", Locale.getDefault().toString());
setDefaultLocale(FileUtil.parseLocale(localeName));
Integer win = preferences.getInt("default.windowsize", 5);
log.info("windowsize from prefs in constructor:" + win);
setWindowSize(win);
project = getLastProject();
setTitle("Yoshikoder Project: " + getProjectFileName());
highlightColor = Color.yellow;
tokenizationCache = new TokenCache();
makeGUI();
}
public void editDocument(){
boolean changed = docPanel.editDocument();
if (changed)
setUnsavedChanges(true);
}
public Charset getDefaultEncoding() {
return defaultEncoding;
}
public void setDefaultEncoding(Charset encoding) {
defaultEncoding = encoding;
}
public Locale getDefaultLocale() {
return defaultLocale;
}
public void setDefaultLocale(Locale loc) {
defaultLocale = loc;
}
public boolean hasUnsavedChanges(){
return unsavedChanges;
}
public void setUnsavedChanges(boolean b){
if (FileUtil.isMac())
getRootPane().putClientProperty("Window.documentModified", b);
unsavedChanges = b;
}
public DocumentList getDocumentList(){
return project.getDocumentList();
}
public YKDictionary getDictionary() {
return project.getDictionary();
}
public void setDictionary(YKDictionary dict){
project.setDictionary(dict);
// update ui
dictPanel.setDictionary(dict);
}
public Font getDisplayFont() {
return project.getDisplayFont();
}
public void setDisplayFont(Font f){
project.setDisplayFont(f);
// update ui
concPanel.setDisplayFont(f);
dictPanel.setDisplayFont(f);
docPanel.setDisplayFont(f);
}
protected String getProjectFileName(){
File f = project.getLocation();
if (f != null)
return f.getAbsolutePath();
else
return "not saved";
}
public YKProject getProject() {
return project;
}
public void setProject(YKProject proj) {
getTokenizationCache().clear();
setUnsavedChanges(false);
project = proj;
Font pfont = project.getDisplayFont();
// update ui
docPanel.setDocumentList(project.getDocumentList());
docPanel.setDisplayFont(pfont);
dictPanel.setDictionary(project.getDictionary());
dictPanel.setDisplayFont(pfont);
concPanel.setConcordance(null);
concPanel.setDisplayFont(pfont);
setTitle("Yoshikoder Project: " + getProjectFileName());
}
public Color getHighlightColor(){
return highlightColor;
}
public void setHighlightColor(Color col){
highlightColor = col;
}
public Concordance getConcordance(){
return concPanel.getConcordance();
}
public void setConcordance(Concordance conc){
concPanel.setConcordance(conc);
}
public YKDocument getSelectedDocument(){
return docPanel.getSelectedDocument();
}
public YKDocument[] getSelectedDocuments(){
return docPanel.getSelectedDocuments();
}
public void setSelectedDocument(YKDocument doc){
docPanel.setSelectedDocument(doc);
}
public Node getSelectedNode(){
return dictPanel.getSelectedNode();
}
public void setSelectedNode(Node n){
dictPanel.setSelectedNode(n);
}
public int getWindowSize() {
return windowSize;
//return project.getDictionary().getWindowSize();
}
public void setWindowSize(int ws) {
windowSize = ws;
//project.getDictionary().setWindowSize(ws);
}
public TokenizationCache getTokenizationCache(){
return tokenizationCache;
}
public void setTokenizationCache(TokenizationCache tc){
tokenizationCache = tc;
}
// basic actions
public void addHighlights(TokenList tl){
docPanel.addHighlights(tl, highlightColor);
}
public void removeHighlights(){
docPanel.removeHighlights();
}
protected FileDialog projectSavingDialog;
public void saveProject(boolean saveAs) throws IOException {
if (projectSavingDialog == null){
projectSavingDialog = DialogUtil.makeFileDialog(Yoshikoder.this, "Save Project", FileDialog.SAVE, null);
projectSavingDialog.setFile(null);
}
File pf = getProject().getLocation();
if (pf != null && saveAs){
projectSavingDialog.setVisible(true);
String file = projectSavingDialog.getFile();
if (file != null){
File asFile = new File(projectSavingDialog.getDirectory(), FileUtil.suffix(file, "ykp"));
project.saveAsXml(asFile);
// don't alter location
} else {
return;
}
} else if (pf != null && !saveAs){
project.saveAsXml(pf);
} else if (pf == null && saveAs){
projectSavingDialog.setVisible(true);
String file = projectSavingDialog.getFile();
if (file != null){
File asFile = new File(projectSavingDialog.getDirectory(), FileUtil.suffix(file, "ykp"));
project.saveAsXml(asFile);
// *don't* alter (lack of) location
} else {
return;
}
} else if (pf == null && !saveAs){
projectSavingDialog.setVisible(true);
String file = projectSavingDialog.getFile();
if (file != null){
File asFile = new File(projectSavingDialog.getDirectory(), FileUtil.suffix(file, "ykp"));
project.setLocation(asFile); // alter location
project.saveAsXml(asFile);
setUnsavedChanges(false);
setTitle("Yoshikoder Project: " + getProjectFileName());
} else {
return;
}
}
}
public void addDocument(YKDocument doc){
project.addDocument(doc);
// update ui
docPanel.addDocument(doc);
}
public void removeDocument(YKDocument doc){
log.info("documentlist before removal" + project.getDocumentList().toString());
project.removeDocument(doc);
// update ui
docPanel.removeDocument(doc);
tokenizationCache.removeTokenList(doc);
log.info("documentlist after removal " + project.getDocumentList().toString());
}
public void addCategory(CategoryNode cat, CategoryNode parent) throws DuplicateException {
project.getDictionary().addCategory(cat, parent);
}
public void addPattern(PatternNode pat, CategoryNode parent) throws DuplicateException {
project.getDictionary().addPattern(pat, parent);
}
public void replaceNode(Node node, Node replacement) throws DuplicateException{
project.getDictionary().replace(node, replacement);
}
public void removeNode(Node n){
project.getDictionary().remove(n);
}
protected YKProject getLastProject(){
Preferences preferences =
Preferences.userNodeForPackage(Yoshikoder.class);
String lastProject = preferences.get("last.project", null);
log.info("last.project is " + lastProject);
if (lastProject != null){
File f = new File(lastProject);
if (f.exists()){
try {
YKProject proj = ImportUtil.importYKProject(f);
return proj;
} catch (Exception ioe){
log.log(Level.WARNING, "Could not load " + lastProject, ioe);
}
} else {
log.warning(lastProject + "does not exist");
}
}
log.info("returning new fresh project");
return new YKProject();
}
protected void platformSpecificSetup(){
setupWindows();
}
protected void setupWindows(){
finishAction = new ExitAction(this);
aboutAction = new WindowsAboutAction(this);
}
protected JMenu makeFileMenu(){
JMenu fileMenu = DialogUtil.makeMenu("Yoshikoder.fileMenu");
fileMenu.add(newProjectAction);
fileMenu.add(openProjectAction);
fileMenu.addSeparator();
fileMenu.add(saveProjectAction);
fileMenu.add(saveAsProjectAction);
JMenu em = DialogUtil.makeMenu("Yoshikoder.projectExportMenu");
em.add(exportProjectAction);
fileMenu.add(em);
fileMenu.addSeparator();
fileMenu.add(preferencesAction);
// these two get removed again in the OSX subclass
fileMenu.addSeparator();
fileMenu.add(finishAction);
return fileMenu;
}
protected JMenu makeDictionaryMenu(){
JMenu dictMenu = DialogUtil.makeMenu("Yoshikoder.dictionaryMenu");
dictMenu.add(addCategoryAction);
dictMenu.add(addPatternAction);
dictMenu.add(addWordsToCategoryAction);
dictMenu.add(editNodeAction);
dictMenu.add(removeNodeAction);
dictMenu.addSeparator();
dictMenu.add(importYKAction);
dictMenu.add(saveAsDictionaryAction);
dictMenu.addSeparator();
//JMenu m = DialogUtil.makeMenu("Yoshikoder.dictionaryImportMenu");
//m.add(importVBProAction);
//dictMenu.add(m);
JMenu m = DialogUtil.makeMenu("Yoshikoder.dictionaryExportMenu");
m.add(exportDictionaryAsHtmlAction);
dictMenu.add(m);
return dictMenu;
}
protected JMenu makeDocumentMenu(){
JMenu docMenu = DialogUtil.makeMenu("Yoshikoder.documentMenu");
docMenu.add(addDocumentAction);
docMenu.add(editDocumentAction);
docMenu.add(importDocumentAction);
docMenu.add(removeDocumentAction);
docMenu.addSeparator();
JMenu em = DialogUtil.makeMenu("Yoshikoder.documentExportMenu");
em.add(exportDocumentAsUTF8Action);
em.add(exportDocumentAsUTF16Action);
docMenu.add(em);
return docMenu;
}
protected JMenu makeConcordanceMenu(){
JMenu concMenu = DialogUtil.makeMenu("Yoshikoder.concordanceMenu");
concMenu.add(makeConcordanceAction);
concMenu.add(openConcordanceAction);
concMenu.addSeparator();
concMenu.add(saveConcordanceAction);
JMenu ce = DialogUtil.makeMenu("Yoshikoder.concordanceExportMenu");
ce.add(exportConcordanceAsHtmlAction);
ce.add(exportConcordanceAsExcelAction);
concMenu.add(ce);
return concMenu;
}
protected JMenu makeHighlightMenu(){
JMenu highMenu = DialogUtil.makeMenu("Yoshikoder.highlightMenu");
highMenu.add(addHighlightsAction);
highMenu.add(removeHighlightsAction);
highMenu.addSeparator();
highMenu.add(setHighlightColorAction);
return highMenu;
}
protected JMenu makeReportMenu(){
JMenu reportMenu = DialogUtil.makeMenu("Yoshikoder.reportMenu");
//reportMenu.addSeparator();
JMenu dictMenu = new JMenu("Apply Dictionary");
reportMenu.add(dictMenu);
dictMenu.add(dictionaryReportAction);
dictMenu.add(unifiedDictionaryReport);
dictMenu.add(statisticalComparisonReportAction);
//dictMenu.add(comparisonReportAction);
dictMenu.addSeparator();
dictMenu.add(concordanceReportAction);
dictMenu.add(multipleConcordanceFrequencyReportAction);
//reportMenu.add(wordscoringAction);
JMenu freqMenu = new JMenu("Count Words");
reportMenu.add(freqMenu);
freqMenu.add(documentReportAction);
//reportMenu.add(allDocumentsReportAction);
freqMenu.add(unifiedDocumentReport);
reportMenu.addSeparator();
reportMenu.add(duplicateReportAction);
return reportMenu;
}
protected JMenu makeHelpMenu(){
JMenu helpMenu = DialogUtil.makeMenu("Yoshikoder.helpMenu");
//if (FileUtil.isMac())
// helpAction = new MacHelpAction(this);
//else
helpAction = new HelpAction(this);
helpMenu.add(helpAction);
helpMenu.add(consoleAction);
helpMenu.addSeparator();
helpMenu.add(licenseAction);
helpMenu.addSeparator();
helpMenu.add(aboutAction);
return helpMenu;
}
protected JToolBar makeToolbar(){
JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL);
toolbar.setFloatable(false);
toolbar.setRollover(true);
toolbar.add(DialogUtil.makeToolbarButton(addCategoryAction, "Toolbar.addCategory.icon"));
toolbar.add(DialogUtil.makeToolbarButton(addPatternAction, "Toolbar.addPattern.icon"));
toolbar.add(DialogUtil.makeToolbarButton(removeNodeAction, "Toolbar.removeNode.icon"));
toolbar.addSeparator();
toolbar.add(DialogUtil.makeToolbarButton(addHighlightsAction, "Toolbar.addHighlights.icon"));
toolbar.add(DialogUtil.makeToolbarButton(makeConcordanceAction, "Toolbar.makeConcordance.icon"));
toolbar.addSeparator();
toolbar.add(DialogUtil.makeToolbarButton(addDocumentAction, "Toolbar.addDocument.icon"));
toolbar.add(DialogUtil.makeToolbarButton(importDocumentAction, "Toolbar.importDocument.icon"));
return toolbar;
}
protected MouseListener makePopupListener(){
final JPopupMenu popup = new JPopupMenu();
popup.add(addCategoryAction);
popup.add(addPatternAction);
popup.add(editNodeAction);
popup.add(addWordsToCategoryAction);
popup.addSeparator();
popup.add(removeNodeAction);
popup.addSeparator();
popup.add(addHighlightsAction);
popup.add(makeConcordanceAction);
MouseAdapter adapt = new MouseAdapter() {
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger())
popup.show(e.getComponent(), e.getX(), e.getY());
}
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
};
return adapt;
}
protected void makeGUI() {
// components
Font pfont = project.getDisplayFont();
dictPanel = new DictionaryPanel(project.getDictionary());
dictPanel.setDisplayFont(pfont);
docPanel = new DocumentPanel(this, project.getDocumentList());
docPanel.setDisplayFont(pfont);
concPanel = new ListConcordancePanel();
concPanel.setDisplayFont(pfont);
platformSpecificSetup();
// actions
//if (FileUtil.isMac())
// setupMac();
//else if (FileUtil.isWindows())
// setupWindows();
//else
// setupOther();
// menus (more platform specific stuff here maybe)
JMenuBar bar = new JMenuBar();
bar.add(makeFileMenu());
bar.add(makeDictionaryMenu());
bar.add(makeDocumentMenu());
bar.add(makeHighlightMenu());
bar.add(makeConcordanceMenu());
bar.add(makeReportMenu());
bar.add(makeHelpMenu());
setJMenuBar(bar);
// the right click popup menu
MouseListener popupListener = makePopupListener();
dictPanel.getTree().addMouseListener(popupListener); // why!
// toolbar
JToolBar toolbar = makeToolbar();
// page layout
JSplitPane split1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
split1.setBorder(null);
dictPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
docPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
split1.setLeftComponent(dictPanel);
split1.setRightComponent(docPanel);
split1.setDividerSize(2);
JSplitPane split2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
split2.setTopComponent(split1);
concPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
split2.setBottomComponent(concPanel);
split2.setDividerSize(2);
split2.setDividerLocation(0.5);
// arrangement
getContentPane().setLayout(new BorderLayout());
getContentPane().add(toolbar, BorderLayout.NORTH);
getContentPane().add(split2, BorderLayout.CENTER);
pack();
// screen placement
Dimension pref = getPreferredSize();
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
setLocation(d.width / 2 - pref.width / 2, d.height / 2 - pref.height / 2);
// on quit
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
finishAction.actionPerformed(null);
}
});
}
public static void main(String[] args) {
//YKFS ykfs = YKFS.getYKFS(); // prepares file system local storage as side effect
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
log.log(Level.WARNING, "could not set appropriate look and feel", e);
}
Yoshikoder yk = null;
try {
if (FileUtil.isMac())
yk = new YoshikoderOSX();
else
yk = new Yoshikoder();
yk.setVisible(true);
} catch (Throwable e) {
StringBuffer sb = new StringBuffer();
sb.append(Messages.getString("Yoshikoder.uncaughtErrorMessage"));
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw)); // show the user
String message = sb.toString();
log.log(Level.SEVERE, message, e);
message = message.replaceAll("\n", "<br>\n");
FatalErrorPanel panel = new FatalErrorPanel(message);
JOptionPane.showMessageDialog(yk, panel,
Messages.getString("Yoshikoder.uncaughtError.0"),
JOptionPane.PLAIN_MESSAGE);
System.exit(1);
} finally {
//
}
}
}