/** * @version $Id: ExtractRelationDialog.java 1839 2014-04-16 02:33:51Z yukihiro-kinjyo $ * * 2013/01/07 15:48:24 * @author s.takuro * * Copyright 2011-2014 TIDAコンソーシアム All Rights Reserved. */ package com.tida_okinawa.corona.ui.editors.extract; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import com.tida_okinawa.corona.correction.parsing.model.DicPatternUtil; import com.tida_okinawa.corona.correction.parsing.model.Modification; import com.tida_okinawa.corona.correction.parsing.model.ModificationElement; import com.tida_okinawa.corona.correction.parsing.model.PatternContainer; import com.tida_okinawa.corona.correction.parsing.model.PatternRecord; import com.tida_okinawa.corona.correction.parsing.model.SearchScopeType; import com.tida_okinawa.corona.correction.parsing.model.Sequence; import com.tida_okinawa.corona.correction.parsing.model.Term; import com.tida_okinawa.corona.io.IoActivator; import com.tida_okinawa.corona.io.model.MorphemeElement; import com.tida_okinawa.corona.io.model.dic.ICoronaDic; import com.tida_okinawa.corona.io.model.dic.IDicName; import com.tida_okinawa.corona.io.model.dic.IPatternDic; import com.tida_okinawa.corona.io.model.dic.TermClass; import com.tida_okinawa.corona.io.model.dic.TermPart; /** * @author s.takuro * #177 パターン自動生成(係り受け抽出) */ public class ExtractRelationDialog extends Dialog { private final int BTN_CLOSE_ID = 100; private final int BTN_ENTRY_ID = 200; private ArrayContentProvider morphemeListTableContentProvider = null; private IMorphemeListTableLabelProvider morphemeListTableLabelProvider = null; private ArrayContentProvider morphemeDetailTableContentProvider = null; private IMorphemeDetailTableLabelProvider morphemeDetailTableLabelProvider = null; private List<IDicName> dicNames = null; /** * 係り受け抽出UI(ダイアログ) * * @param parentShell * 親シェル */ public ExtractRelationDialog(Shell parentShell) { super(parentShell); prevDetailMap = new HashMap<String, IExtractRelationElement>(); } /** 新規Shellオブジェクト構成 */ @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText(Messages.EXTRACT_RELATION_TITLE); } /** * 係り受け抽出一覧のコンテンツ・プロバイダーの設定 * * @param provider * TableViewerに設定するContentProvider */ public void setMorphemeListTableContentProvider(ArrayContentProvider provider) { this.morphemeListTableContentProvider = provider; } /** * 係り受け抽出一覧のラベル・プロバイダーの設定 * * @param provider * TableViewerに設定するLabelProvider */ public void setMorphemeListTableLabelProvider(IMorphemeListTableLabelProvider provider) { this.morphemeListTableLabelProvider = provider; } /** * 係り受け抽出詳細のコンテンツ・プロバイダーの設定 * * @param provider * TreeViewerに設定するContentProvider */ public void setMorphemeDetailTreeContentProvider(ArrayContentProvider provider) { this.morphemeDetailTableContentProvider = provider; } /** * 係り受け抽出詳細のラベル・プロバイダーの設定 * * @param provider * TreeViewerに設定するLabelProvider */ public void setMorphemeDetailTableLabelProvider(IMorphemeDetailTableLabelProvider provider) { this.morphemeDetailTableLabelProvider = provider; } /** * 全パターン辞書名の設定 * * @param dicNames * 辞書名一覧 */ public void setDicName(List<IDicName> dicNames) { this.dicNames = dicNames; } /** * 係り受け抽出結果の設定 * * @param extractRelationList * 係り受け抽出結果 */ public void setExtractRelationList(IExtractRelationElement[] extractRelationList) { this.extractRelationList = extractRelationList; } /* **************************************** * UI */ @Override protected Control createDialogArea(Composite parent) { /* マスター全体の作成 */ Composite compAll = new Composite(parent, SWT.NONE); compAll.setLayout(new GridLayout()); compAll.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); /* マスター上部の作成 */ Composite compTitle = new Composite(compAll, SWT.NONE); compTitle.setLayout(new GridLayout()); compTitle.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false)); setTopComposite(compTitle); Label labelSeparator1 = new Label(compAll, SWT.SEPARATOR | SWT.HORIZONTAL); labelSeparator1.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false)); /* マスター中央部の作成 */ Composite compMiddle = new Composite(compAll, SWT.NONE); compMiddle.setLayout(new GridLayout(2, false)); compMiddle.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); setMiddleComposite(compMiddle); /* マスター下部の作成(セパレート表示) */ Composite compButton = new Composite(compAll, SWT.NONE); compButton.setLayout(new GridLayout()); compButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Label labelSeparator2 = new Label(compButton, SWT.SEPARATOR | SWT.HORIZONTAL); labelSeparator2.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); return parent; } /** * 上部の領域のコンポーネント配置 * * @param parent * 親Composite */ @SuppressWarnings("static-method") private void setTopComposite(Composite parent) { Label labelOutline = new Label(parent, SWT.NONE); labelOutline.setText(Messages.EXTRACT_RELATION_OUTLINE); } /** * 中央の領域のコンポーネント配置 * * @param parent * 親Composite */ private void setMiddleComposite(Composite parent) { /* 上側のコンボボックス用のComposite作成 */ Composite compCombo = new Composite(parent, SWT.NONE); compCombo.setLayout(new GridLayout()); GridData layoutCombo = new GridData(SWT.END, SWT.NONE, false, false); layoutCombo.horizontalSpan = 2; compCombo.setLayoutData(layoutCombo); setComboSaved(compCombo); /* 左側のテーブル用のComposite作成 */ Composite compTableResult = new Composite(parent, SWT.NONE); compTableResult.setLayout(new GridLayout()); GridData layoutLeft = new GridData(SWT.FILL, SWT.FILL, true, true); layoutLeft.heightHint = 400; compTableResult.setLayoutData(layoutLeft); Label labelResult = new Label(compTableResult, SWT.NONE); labelResult.setText(Messages.EXTRACT_RERATION_LABEL_RESULT); setMorphemeListTable(compTableResult); /* 右側のテーブル用のComposite作成 */ Composite compTableDetail = new Composite(parent, SWT.NONE); compTableDetail.setLayout(new GridLayout()); GridData layoutRight = new GridData(SWT.FILL, SWT.FILL, true, true); layoutRight.heightHint = 400; compTableDetail.setLayoutData(layoutRight); Label labelDetail = new Label(compTableDetail, SWT.NONE); labelDetail.setText(Messages.EXTRACT_RERATION_LABEL_DETAIL); setMorphemeDetailTree(compTableDetail); } /* **************************************** * Component */ private TableViewer morphemeListTableViewer = null; private TableViewer morphemeDetailTableViewer = null; private Combo comboToSave = null; private Button okButton = null; /** * 保存先選択用コンボ * * @param parent * 親Composite */ private void setComboSaved(Composite parent) { Composite compCombo = new Composite(parent, SWT.NONE); compCombo.setLayout(new GridLayout(2, false)); compCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); Label labelCombo = new Label(compCombo, SWT.NONE); labelCombo.setText(Messages.EXTRACT_RELATION_COMBO_ENTRY); comboToSave = new Combo(compCombo, SWT.READ_ONLY); comboToSave.setItems(createComboItem()); comboToSave.select(0); } /** * 係り受け一覧表示用テーブル * * @param parent * 親Composite */ private void setMorphemeListTable(Composite parent) { morphemeListTableViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE | SWT.VIRTUAL | SWT.BORDER); Table morphemeListTable = morphemeListTableViewer.getTable(); GridData gdMorphemeList = new GridData(GridData.FILL, SWT.FILL, true, true); morphemeListTable.setToolTipText(""); //$NON-NLS-1$ morphemeListTable.setHeaderVisible(true); morphemeListTable.setLinesVisible(true); morphemeListTable.setLayoutData(gdMorphemeList); createTableColumn(morphemeListTable, SWT.LEFT, Messages.EXTRACT_RELATION_TABLE_COLUMN_NUMBER, 70); createTableColumn(morphemeListTable, SWT.LEFT, Messages.EXTRACT_RELATION_TABLE_COLUMN_RECENT, 180); createTableColumn(morphemeListTable, SWT.LEFT, Messages.EXTRACT_RELATION_TABLE_COLUMN_DEST, 180); morphemeListTableViewer.setLabelProvider(morphemeListTableLabelProvider); morphemeDetailTableLabelProvider.setPrevDetailMap(prevDetailMap); morphemeListTableViewer.setContentProvider(morphemeListTableContentProvider); morphemeListTableViewer.setInput(extractRelationList); morphemeListTableViewer.addSelectionChangedListener(new ISelectionChangedListener() { IStructuredSelection prevSelection = new StructuredSelection(); @Override public void selectionChanged(SelectionChangedEvent event) { if (event.getSelection() instanceof IStructuredSelection) { IStructuredSelection ss = (IStructuredSelection) event.getSelection(); if (!prevSelection.equals(ss)) { Object o = ((IStructuredSelection) event.getSelection()).getFirstElement(); if (o instanceof IExtractRelationElement) { ExtractRelationElement ere = (ExtractRelationElement) o; ExtractRelation extractRelation = new ExtractRelation(); extractRelationDetail = extractRelation.createRelationDetail(ere); morphemeDetailTableLabelProvider.setPrevDetailMap(prevDetailMap); morphemeDetailTableViewer.setInput(extractRelationDetail); prevSelection = ss; selectedRelation = ere; /* 詳細画面が切り替わるので登録ボタンも無効化 */ okButton.setEnabled(false); } } } } }); } /** * 係り受け詳細表示用ツリー * * @param parent * 親Composite */ private void setMorphemeDetailTree(Composite parent) { morphemeDetailTableViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.SINGLE | SWT.CHECK | SWT.BORDER); GridData gdMorphemeDetail = new GridData(GridData.FILL, SWT.FILL, true, true); Table morphemeDetailTable = morphemeDetailTableViewer.getTable(); morphemeDetailTable.setToolTipText(""); //$NON-NLS-1$ morphemeDetailTable.setLayoutData(gdMorphemeDetail); morphemeDetailTable.setHeaderVisible(true); morphemeDetailTable.setLinesVisible(true); createTableColumn(morphemeDetailTable, SWT.LEFT, "", 30); //$NON-NLS-1$ createTableColumn(morphemeDetailTable, SWT.LEFT, Messages.EXTRACT_RELATION_TABLE_COLUMN_RECENT, 200); createTableColumn(morphemeDetailTable, SWT.LEFT, Messages.EXTRACT_RELATION_TABLE_COLUMN_DEST, 200); morphemeDetailTableViewer.setLabelProvider(morphemeDetailTableLabelProvider); morphemeDetailTableViewer.setContentProvider(morphemeDetailTableContentProvider); morphemeDetailTableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { /* 登録アイテムが選択されているかの判定 */ boolean selected = false; Table table = morphemeDetailTableViewer.getTable(); for (int i = 0; i < table.getItemCount(); i++) { if (table.getItem(i).getChecked()) { selected = true; break; } } if (selected) { okButton.setEnabled(true); } else { okButton.setEnabled(false); } } }); } /** * テーブルのカラムの生成 * * @param table * テーブル * @param style * スタイル * @param title * タイトル * @param width * 横幅 * @return TableColumn */ @SuppressWarnings("static-method") private TableColumn createTableColumn(Table table, int style, String title, int width) { TableColumn tc = new TableColumn(table, style); tc.setText(title); tc.setResizable(true); tc.setWidth(width); return tc; } /** ボタン・バーの設定 */ @Override protected void createButtonsForButtonBar(Composite parent) { okButton = createButton(parent, BTN_ENTRY_ID, Messages.EXTRACT_RELATION_BUTTON_ENTRY, true); okButton.setEnabled(false); createButton(parent, BTN_CLOSE_ID, Messages.EXTRACT_RELATION_BUTTON_CLOSE, true); } @Override protected void buttonPressed(int buttonId) { switch (buttonId) { case BTN_ENTRY_ID: if (questionMessageBox(getShell(), Messages.EXTRACT_RELATION_CREATE_CHECK) == SWT.OK) { ErrorNumber result = entryPatternAction(); if (result == ErrorNumber.RESULT_CREATE_OK) { morphemeListTableViewer.update(extractRelationList, null); morphemeDetailTableViewer.update(extractRelationDetail, null); } else if (result == ErrorNumber.RESULT_NOT_PATTERNDIC) { errorMessageBox(getShell(), Messages.EXTRACT_ERROR_NOT_PATTERNDIC); } else if (result == ErrorNumber.RESULT_NOT_SAVELOCATION) { warningMessageBox(getShell(), Messages.EXTRACT_ERROR_NOT_SAVE_LOCATION); } else if (result == ErrorNumber.RESULT_NOT_CHECK) { warningMessageBox(getShell(), Messages.EXTRACT_ERROR_NOT_CHOOSE); } else { errorMessageBox(getShell(), Messages.EXTRACT_ERROR_NOT_CREATE_PATTERN); } } break; case BTN_CLOSE_ID: setReturnCode(BTN_CLOSE_ID); close(); break; default: super.buttonPressed(buttonId); break; } } /* **************************************** * 登録 */ private enum ErrorNumber { RESULT_CREATE_OK, RESULT_CREATE_NG, RESULT_NOT_PATTERNDIC, RESULT_NOT_SAVELOCATION, RESULT_NOT_CHECK } /* 登録時に、登録された要素を持つリストの色を変更させる為に使用 */ Map<String, IExtractRelationElement> prevDetailMap = null; private IExtractRelationElement selectedRelation = null; private IExtractRelationElement[] extractRelationList = null; private IExtractRelationElement[] extractRelationDetail = null; /* パターン辞書に登録 */ private ErrorNumber entryPatternAction() { if ("".equals(comboToSave.getText())) { //$NON-NLS-1$ /* 保存先が未選択の場合 */ return ErrorNumber.RESULT_NOT_SAVELOCATION; } boolean chk = false; /* Comboで選択中の登録先辞書を取得 */ IDicName dicName = dicNames.get(comboToSave.getSelectionIndex()); ICoronaDic coronaDic = IoActivator.getService().getDictionary(dicName.getDicId()); if (!(coronaDic instanceof IPatternDic)) { /* パターン辞書の取得に失敗した場合 */ return ErrorNumber.RESULT_NOT_PATTERNDIC; } Table table = morphemeDetailTableViewer.getTable(); /* チェックの入った項目をパターンレコード一覧に追加 */ for (int i = 0; i < table.getItemCount(); i++) { if (table.getItem(i).getChecked()) { IExtractRelationElement ereDetail = extractRelationDetail[i]; PatternRecord input = createPatternRecord(ereDetail); if (input != null) { chk = true; /* 登録パターンに設定する */ input.getIPattern().setDirty(true); ((IPatternDic) coronaDic).addItem(input.getIPattern()); /* 登録済みアイテムに設定する */ selectedRelation.setCompletion(true); ereDetail.setCompletion(true); StringBuilder ereHyouki = new StringBuilder(100); ereHyouki.append(ereDetail.getHyouki()).append(" ").append(ereDetail.getDependDestination().getHyouki()); if (!prevDetailMap.containsKey(ereHyouki.toString())) { prevDetailMap.put(ereHyouki.toString(), ereDetail); } /* 登録した係り受けはチェックを外す(同時に登録ボタンも無効化) */ table.getItem(i).setChecked(false); okButton.setEnabled(false); } } } if (!chk) { /* 一つもチェックが入っていなければエラーメッセージを表示してリターン */ return ErrorNumber.RESULT_NOT_CHECK; } /* コミット */ if (coronaDic.commit(new NullProgressMonitor()) != true) { return ErrorNumber.RESULT_CREATE_NG; } return ErrorNumber.RESULT_CREATE_OK; } /** * パターンレコードの生成 * * @param ere * 係り受け抽出した要素 * @return パターンレコード */ private PatternRecord createPatternRecord(IExtractRelationElement ere) { /* 係り受けを持つパターンの作成 */ String name = ere.getHyouki() + ere.getDependDestination().getHyouki(); PatternRecord record = new PatternRecord(IoActivator.getDicFactory().createPattern(name, "", -99, false)); //$NON-NLS-1$ Modification modification = new Modification(record, true); record.addChild(modification); /* 係り元/係り先の作成 */ createMorphemeElement(modification.getSource(), ere); createMorphemeElement(modification.getDestination(), ere.getDependDestination()); /* レコードをテキストに変換して登録(レコードに子を追加しただけではデータベースに登録されないので) */ String convertString = DicPatternUtil.convertFrom(record); record.getIPattern().setText(convertString); return record; } /** * 形態素の要素を作成 * * @param modificationElement * 係り元、係り先パターンを表すクラス * @param ere * 係り受け抽出した要素 */ private void createMorphemeElement(ModificationElement modificationElement, IExtractRelationElement ere) { List<MorphemeElement> morphemes = ere.getMorphemes(); PatternContainer sequence = null; /* 形態素が0個の場合は、ワイルドカードなので空の単語を追加 */ if (morphemes.size() == 0) { Term empty = new Term(modificationElement); modificationElement.addChild(empty); return; } /* 形態素が1個の場合 */ else if (morphemes.size() == 1) { for (MorphemeElement morpheme : morphemes) { if (morpheme != null) { Term single = new Term(modificationElement); createTermElement(single, morpheme); modificationElement.addChild(single); return; } } } /* 形態素が1個より多い場合 */ else { /* 連続の要素を追加 */ sequence = new Sequence(modificationElement); modificationElement.addChild(sequence); /* "文節"に設定 */ ((Sequence) sequence).setScope(SearchScopeType.SEARCH_SEGMENT); for (MorphemeElement morpheme : morphemes) { Term multi = new Term(sequence); /* nullの場合は空の単語を追加する */ if (morpheme != null) { createTermElement(multi, morpheme); } sequence.addChild(multi); } } } /** * 単語(Word)の要素を追加 * * @param term * 単語 * @param morpheme * 係り受け */ @SuppressWarnings("static-method") private void createTermElement(Term term, MorphemeElement morpheme) { term.setWord(morpheme.getGenkei()); term.setPart(TermPart.valueOfName(morpheme.getHinshi())); term.setWordClass(TermClass.valueOfName(morpheme.getHinshiSaibunrui())); term.setHitElement(morpheme); } /* **************************************** * その他 */ /* Comboのアイテムを生成 */ private String[] createComboItem() { if (dicNames == null) { return null; } int cnt = 0; String[] comboItem = new String[dicNames.size()]; /* 辞書アイテムのソート(辞書名で昇順) */ Collections.sort(dicNames, new Comparator<IDicName>() { @Override public int compare(IDicName o1, IDicName o2) { String s1 = o1.getDicName(); String s2 = o2.getDicName(); return s1.compareToIgnoreCase(s2); } }); /* 辞書選択コンボにアイテム追加 */ for (IDicName name : dicNames) { comboItem[cnt++] = name.getDicName(); } return comboItem; } /* メッセージボックス(確認用) */ private static int questionMessageBox(Shell shell, String message) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL); messageBox.setText(Messages.EXTRACT_MESSAGEBOX_TEXT_QUESTION); messageBox.setMessage(message); return messageBox.open(); } /* メッセージボックス(要注意) */ private static void warningMessageBox(Shell shell, String message) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK); messageBox.setText(Messages.EXTRACT_MESSAGEBOX_TEXT_WARNING); messageBox.setMessage(message); messageBox.open(); } /* メッセージボックス(エラー) */ private static void errorMessageBox(Shell shell, String message) { MessageBox messageBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK); messageBox.setText(Messages.EXTRACT_MESSAGEBOX_TEXT_ERROR); messageBox.setMessage(message); messageBox.open(); } @Override protected boolean isResizable() { return true; } }