• R/O
  • SSH
  • HTTPS

chaki: Commit


Commit MetaInfo

Revision614 (tree)
Time2018-07-22 21:19:52
Authortomorita

Log Message

Cradleの更新によりMWE Download処理を改訂.
MWE自動アノテーションのLink生成ロジックを変更(Segment間Linkではなく係り受けに反映).
残務:二重にSegmentが生成されるのを防ぐ.

Change Summary

Incremental Difference

--- trunk/ChaKi.NET/src/Timings/Properties/AssemblyInfo.cs (revision 613)
+++ trunk/ChaKi.NET/src/Timings/Properties/AssemblyInfo.cs (revision 614)
@@ -12,5 +12,5 @@
1212 [assembly: AssemblyCulture("")]
1313 [assembly: ComVisible(false)]
1414 [assembly: Guid("ff6652ed-b932-466b-944b-ce88d698979b")]
15-[assembly: AssemblyVersion("3.11.612.0")]
16-[assembly: AssemblyFileVersion("3.11.612.0")]
15+[assembly: AssemblyVersion("3.11.613.0")]
16+[assembly: AssemblyFileVersion("3.11.613.0")]
--- trunk/ChaKi.NET/src/Entity/Corpora/MWE.cs (revision 613)
+++ trunk/ChaKi.NET/src/Entity/Corpora/MWE.cs (revision 614)
@@ -12,6 +12,7 @@
1212 this.Items = new List<MWENode>();
1313 }
1414
15+ public Lexeme Lex { get; set; }
1516 public List<MWENode> Items { get; set; }
1617
1718 public override string ToString()
@@ -45,6 +46,8 @@
4546
4647 public int DependsTo { get; set; }
4748
49+ public string DependsAs { get; set; }
50+
4851 public override string ToString()
4952 {
5053 return this.Label;
--- trunk/ChaKi.NET/src/Service/DependencyEdit/OperationCreateMWEAnnotation.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/DependencyEdit/OperationCreateMWEAnnotation.cs (revision 614)
@@ -1,4 +1,5 @@
1-using ChaKi.Entity.Corpora;
1+using ChaKi.Common.SequenceMatcher;
2+using ChaKi.Entity.Corpora;
23 using ChaKi.Entity.Corpora.Annotations;
34 using System;
45 using System.Collections.Generic;
@@ -7,36 +8,34 @@
78
89 namespace ChaKi.Service.DependencyEdit
910 {
11+ /// <summary>
12+ /// MWEアノテーションの作成
13+ /// Segment, Groupを生成するが、依存関係はLinkとしては生成しない.
14+ /// 依存関係はBunsetsu Segmentの係り受けの変更によって行う.
15+ /// </summary>
1016 internal class OperationCreateMWEAnnotation : Operation
1117 {
12- private List<CharRange> m_CharRanges;
13- private List<int> m_Dependencies;
14- private List<Lexeme> m_SegLexList;
15- private List<Lexeme> m_LinkLexList;
18+ private MatchingResult m_Match;
1619
1720 private const string MWETag = "MWE";
1821
19- public OperationCreateMWEAnnotation(List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList)
22+ private List<ChangeLinkInfo> m_ChangeLinks = new List<ChangeLinkInfo>();
23+
24+ public OperationCreateMWEAnnotation(MatchingResult match)
2025 {
21- m_CharRanges = charRanges;
22- m_Dependencies = dependencies;
23- m_SegLexList = segLexList;
24- m_LinkLexList = linkLexList;
26+ m_Match = match;
2527 }
2628
2729 public override void Execute(DepEditContext ctx)
2830 {
29- ExecuteCore(ctx, m_CharRanges, m_Dependencies, m_SegLexList, m_LinkLexList);
30- }
31-
32- public override void UnExecute(DepEditContext ctx)
33- {
34- OperationDeleteMWEAnnotation.ExecuteCore(ctx, m_CharRanges, m_Dependencies, m_SegLexList, m_LinkLexList);
35- }
36-
37- public static void ExecuteCore(DepEditContext ctx, List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList)
38- {
39- var segs = new List<Segment>();
31+ // 前提条件
32+ var n = m_Match.CharRangeList.Count;
33+ if (n != m_Match.MWE.Items.Count)
34+ {
35+ throw new Exception($"#Ranges={n} != #MWEItems={m_Match.MWE.Items.Count}");
36+ }
37+ // m_CharRangeにそれぞれMWE Segmnetを作成し、全体をGroupとする
38+ var bunsetsulist = new List<Segment>();
4039 var stag = ctx.Proj.FindTag(Tag.SEGMENT, MWETag);
4140 if (stag == null)
4241 {
@@ -59,100 +58,213 @@
5958 grp.User = ctx.User;
6059 grp.Version = grp.Tag.Version;
6160 ctx.Save(grp);
61+ var attr = new GroupAttribute()
62+ {
63+ Target = grp,
64+ Proj = ctx.Proj,
65+ User = ctx.User,
66+ Version = grp.Tag.Version,
67+ Key = "POS",
68+ Value = m_Match.MWE.Lex.PartOfSpeech.ToString(),
69+ };
70+ ctx.Save(attr);
71+ grp.Attributes.Add(attr);
72+ ctx.Flush();
6273 // Segmentを生成し、Groupにadd
63- for (var i = 0; i < charRanges.Count; i++)
74+ for (var i = 0; i < m_Match.CharRangeList.Count; i++)
6475 {
65- var range = charRanges[i];
66- var lex = segLexList[i];
67- if (lex != null)
76+ var range = m_Match.CharRangeList[i];
77+ var docid = ctx.Sen.ParentDoc.ID;
78+ var start = range.Start.Value + ctx.Sen.StartChar;
79+ var end = range.End.Value + ctx.Sen.StartChar;
80+ var seg = new Segment();
81+ seg.Sentence = ctx.Sen;
82+ seg.Tag = stag;
83+ seg.Doc = ctx.Sen.ParentDoc;
84+ seg.StartChar = start;
85+ seg.EndChar = end;
86+ seg.Proj = ctx.Proj;
87+ seg.User = ctx.User;
88+ seg.Version = seg.Tag.Version;
89+ ctx.Save(seg);
90+ grp.Tags.Add(seg);
91+ // Placeholderの場合は、POS attributeを付加
92+ if (m_Match.MWE.Items[i].NodeType == MWENodeType.Placeholder)
6893 {
69- // OperationChangeLexemeと同じ処理でLexemeを登録
70- var props = lex.ToPropertyArray();
71- var customprop = lex.CustomProperty;
72- if (lex.Dictionary != null) // 他のDB内のオブジェクト
94+ var segattr = new SegmentAttribute()
7395 {
74- lex = null; // 一旦nullにしてCreateOrUpdateLexeme()で生成させる.
96+ Target = seg,
97+ Proj = ctx.Proj,
98+ User = ctx.User,
99+ Version = seg.Tag.Version,
100+ Key = "POS",
101+ Value = m_Match.MWE.Items[i].POS,
102+ };
103+ ctx.Save(segattr);
104+ seg.Attributes.Add(segattr);
105+ }
106+ ctx.Flush();
107+
108+ // 同じ範囲にあるBunsetsu Segmentを見つけておく
109+ var bunsetsu = FindSegmentBetween(ctx, docid, start, end);
110+ if (bunsetsu == null)
111+ {
112+ throw new Exception($"No Bunsetsu found at: {range.ToString()}");
113+ }
114+ bunsetsulist.Add(bunsetsu);
115+ }
116+ if (bunsetsulist.Count != n)
117+ {
118+ throw new Exception($"Found only {bunsetsulist.Count} Bunsetsu. Expected:{n}");
119+ }
120+ ctx.Flush();
121+
122+ // BunsetsuのLink(係り受け)をMWE.DepToに基づいて変更する.
123+ for (int i = 0; i < n; i++)
124+ {
125+ var mweitem = m_Match.MWE.Items[i];
126+ if (mweitem.DependsTo < 0 || mweitem.DependsTo >= m_Match.CharRangeList.Count)
127+ {
128+ continue;
129+ }
130+ var buns = bunsetsulist[i];
131+ var link = FindLinkFrom(ctx, buns);
132+ var newrange = m_Match.CharRangeList[mweitem.DependsTo];
133+ var to_buns = FindSegmentBetween(ctx, buns.Doc.ID,
134+ newrange.Start.Value + ctx.Sen.StartChar,
135+ newrange.End.Value + ctx.Sen.StartChar);
136+ if (to_buns == null)
137+ {
138+ throw new Exception($"No Bunsetsu found for {i}-th word: {newrange.ToString()}");
139+ }
140+
141+ // Linkの操作情報を作成
142+ var cli = new ChangeLinkInfo(buns.Doc.ID,
143+ link.From.StartChar, link.From.EndChar, link.From.Tag.Name,
144+ link.To.StartChar, link.To.EndChar, link.To.Tag.Name,
145+ to_buns.StartChar, to_buns.EndChar, to_buns.Tag.Name,
146+ link.Tag.Name, mweitem.DependsAs);
147+ var link_modified = false;
148+ // Link ToSegの付け替え
149+ if (link.To.StartChar != to_buns.StartChar || link.To.EndChar != to_buns.EndChar)
150+ {
151+ link.To = to_buns;
152+ ctx.Flush();
153+ //var q = ctx.Session.CreateSQLQuery($"UPDATE link SET to_segment_id={to_buns.ID} WHERE id={link.ID}");
154+ //q.ExecuteUpdate();
155+ link_modified = true;
156+ }
157+ // Link Tagの付け替え
158+ if (cli.OldLinkTagName != cli.NewLinkTagName
159+ && (string.IsNullOrEmpty(cli.NewLinkTagName) || cli.NewLinkTagName != "_"))
160+ {
161+ var newlinktag = ctx.Proj.FindTag(Tag.LINK, cli.NewLinkTagName);
162+ if (newlinktag == null)
163+ {
164+ var t = new Tag(Tag.LINK, cli.NewLinkTagName) { Parent = ctx.Proj.TagSetList[0], Version = ctx.TSVersion };
165+ ctx.Proj.TagSetList[0].AddTag(t);
166+ newlinktag = t;
167+ ctx.Save(t);
75168 }
76- // Lexemeの依存オブジェクト(Base, POS, CForm, CType)を登録すべきかの判定と処理
77- CreateOrUpdateLexeme(ctx, ref lex, props, customprop);
169+ link.Tag = newlinktag;
78170 ctx.Flush();
171+ link_modified = true;
79172 }
173+
174+ if (link_modified)
175+ {
176+ this.m_ChangeLinks.Add(cli);
177+ }
178+ }
179+ }
180+
181+ public override void UnExecute(DepEditContext ctx)
182+ {
183+ if (m_Match.CharRangeList.Count == 0)
184+ {
185+ throw new Exception("CharRangeList is empty.");
186+ }
187+ var segs = new HashSet<Segment>();
188+ foreach (var range in m_Match.CharRangeList)
189+ {
80190 var docid = ctx.Sen.ParentDoc.ID;
81191 var start = range.Start.Value + ctx.Sen.StartChar;
82192 var end = range.End.Value + ctx.Sen.StartChar;
83- var seg = FindSegmentBetween(ctx, docid, start, end, MWETag);
193+ // この範囲にあるMWE Segmentを見つける
194+ Segment seg = null;
195+ try
196+ {
197+ seg = FindSegmentBetween(ctx, docid, start, end, MWETag);
198+ }
199+ catch (Exception ex)
200+ {
201+ throw new Exception("Duplicated segments disturb undoing!");
202+ }
84203 if (seg == null)
85204 {
86- seg = new Segment();
87- seg.Sentence = ctx.Sen;
88- seg.Tag = stag;
89- seg.Doc = ctx.Sen.ParentDoc;
90- seg.StartChar = start;
91- seg.EndChar = end;
92- seg.Proj = ctx.Proj;
93- seg.User = ctx.User;
94- seg.Version = seg.Tag.Version;
95- if (lex != null)
96- {
97- seg.Lex = lex;
98- }
99- ctx.Save(seg);
100- ctx.Flush();
205+ throw new Exception($"No MWE Segment found at: {range.ToString()}");
101206 }
102207 segs.Add(seg);
103- grp.Tags.Add(seg);
104208 }
105- var ltag = ctx.Proj.FindTag(Tag.LINK, MWETag);
106- if (ltag == null)
209+ // 最初のSegmentを含むGroupを見つける
210+ var grp = FindGroupCovering(ctx, segs, MWETag);
211+ if (grp == null)
107212 {
108- var t = new Tag(Tag.LINK, MWETag) { Parent = ctx.Proj.TagSetList[0], Version = ctx.TSVersion };
109- ctx.Proj.TagSetList[0].AddTag(t);
110- ltag = t;
111- ctx.Save(t);
213+ throw new Exception($"No MWE Group found.");
112214 }
113- for (int i = 0; i < dependencies.Count; i++)
215+ foreach (var attr in grp.Attributes)
114216 {
115- var dep = dependencies[i];
116- if (dep < 0 || dep > segs.Count - 1)
217+ ctx.Delete(attr);
218+ ctx.Flush();
219+ }
220+ grp.Attributes.Clear();
221+ ctx.Delete(grp);
222+ foreach (var seg in segs)
223+ {
224+ foreach (var attr in seg.Attributes)
117225 {
118- continue;
226+ ctx.Delete(attr);
227+ ctx.Flush();
119228 }
120- if (FindLink(ctx, segs[i], segs[dep], MWETag) != null)
229+ seg.Attributes.Clear();
230+ ctx.Delete(seg);
231+ }
232+ // Linkのつけなおし
233+ foreach (var cli in m_ChangeLinks)
234+ {
235+ var fromBuns = FindSegmentBetween(ctx, cli.DocId, cli.LinkFromSegStartPos, cli.LinkFromSegEndPos);
236+ if (fromBuns == null)
121237 {
122- // 同一のLinkは生成しない.
123- continue;
238+ throw new Exception($"No From-Bunsetsu found : {cli.NewFromSegStartPos}-{cli.NewFromSegEndPos}");
124239 }
125- var lex = linkLexList[i];
126- if (lex != null)
240+ var oldToBuns = FindSegmentBetween(ctx, cli.DocId, cli.NewFromSegStartPos, cli.NewFromSegEndPos);
241+ if (oldToBuns == null)
127242 {
128- // OperationChangeLexemeと同じ処理でLexemeを登録
129- var props = lex.ToPropertyArray();
130- var customprop = lex.CustomProperty;
131- if (lex.Dictionary != null) // 他のDB内のオブジェクト
132- {
133- lex = null; // 一旦nullにしてCreateOrUpdateLexeme()で生成させる.
134- }
135- // Lexemeの依存オブジェクト(Base, POS, CForm, CType)を登録すべきかの判定と処理
136- CreateOrUpdateLexeme(ctx, ref lex, props, customprop);
137- ctx.Flush();
243+ throw new Exception($"No To-Bunsetsu(old) found : {cli.NewFromSegStartPos}-{cli.NewFromSegEndPos}");
138244 }
139- var lnk = new Link();
140- lnk.FromSentence = ctx.Sen;
141- lnk.ToSentence = ctx.Sen;
142- lnk.Tag = ltag;
143- lnk.From = segs[i];
144- lnk.To = segs[dep];
145- lnk.Proj = ctx.Proj;
146- lnk.User = ctx.User;
147- lnk.Version = lnk.Tag.Version;
148- if (lex != null)
245+ var newToBuns = FindSegmentBetween(ctx, cli.DocId, cli.NewToSegStartPos, cli.NewToSegEndPos);
246+ if (newToBuns == null)
149247 {
150- lnk.Lex = lex;
248+ throw new Exception($"No To-Bunsetsu(new) found : {cli.NewToSegStartPos}-{cli.NewToSegEndPos}");
151249 }
152- ctx.Save(lnk);
250+ var link = FindLinkFrom(ctx, fromBuns);
251+ if (newToBuns == null)
252+ {
253+ throw new Exception($"No Link found starting at {cli.LinkFromSegStartPos}-{cli.LinkFromSegEndPos}");
254+ }
255+ link.To = oldToBuns;
153256 ctx.Flush();
257+ var orglinktag = ctx.Proj.FindTag(Tag.LINK, cli.OldLinkTagName);
258+ if (orglinktag == null)
259+ {
260+ var t = new Tag(Tag.LINK, cli.NewLinkTagName) { Parent = ctx.Proj.TagSetList[0], Version = ctx.TSVersion };
261+ ctx.Proj.TagSetList[0].AddTag(t);
262+ orglinktag = t;
263+ ctx.Save(t);
264+ }
265+ link.Tag = orglinktag;
266+ ctx.Flush();
154267 }
155- ctx.Flush();
156268 }
157269
158270 public override string ToIronRubyStatement(DepEditContext ctx)
--- trunk/ChaKi.NET/src/Service/DependencyEdit/DepEditService.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/DependencyEdit/DepEditService.cs (revision 614)
@@ -274,28 +274,16 @@
274274 {
275275 throw new InvalidOperationException("Unknown sentence.");
276276 }
277- segs = new List<Segment>();
278- links = new List<Link>();
279277
280278 //TODO: seg.Tag.IDの比較対象はTagSetからBunsetsu Segment TagのIDを引っ張ってくる必要がある。
281- IQuery segquery = m_Context.Session.CreateQuery(
282- string.Format("from Segment seg where seg.Doc.ID = {0} and seg.Sentence.ID = {1} and seg.Tag.Name = 'Bunsetsu' and seg.Proj.ID={2} order by seg.StartChar",
283- m_Context.Sen.ParentDoc.ID, m_Context.Sen.ID, m_Context.Proj.ID));
284- IList sresults = segquery.List();
285- foreach (Object o in sresults)
286- {
287- segs.Add(o as Segment);
288- }
279+ var segquery = m_Context.Session.CreateQuery(
280+ $"from Segment seg where seg.Doc.ID = {m_Context.Sen.ParentDoc.ID} and seg.Sentence.ID = {m_Context.Sen.ID} and seg.Tag.Name = 'Bunsetsu' and seg.Proj.ID={m_Context.Proj.ID} order by seg.StartChar");
281+ segs = segquery.List<Segment>().ToList();
282+ var sids = (from s in segs select s.ID).ToList();
289283
290- ICriteria linkMatching = m_Context.Session.CreateCriteria(typeof(Link))
291- .Add(Restrictions.Or(
292- Restrictions.In("From", sresults),
293- Restrictions.In("To", sresults)));
294- IList lresults = linkMatching.List();
295- foreach (Object o in lresults)
296- {
297- links.Add(o as Link);
298- }
284+ var q = m_Context.Session.CreateQuery(
285+ $"from Link lnk where lnk.From.ID in {Util.BuildIDList(sids)} or lnk.To.ID in {Util.BuildIDList(sids)}");
286+ links = q.List<Link>().ToList();
299287 }
300288
301289 /// <summary>
@@ -641,20 +629,61 @@
641629 m_History.Record(op);
642630 }
643631
644- public void CreateMWEAnnotation(List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList)
632+ public IOperation CreateMWEAnnotation(MatchingResult match, bool recordHistory = true)
645633 {
646- var op = new OperationCreateMWEAnnotation(charRanges, dependencies, segLexList, linkLexList);
634+ var op = new OperationCreateMWEAnnotation(match);
647635 op.Execute(m_Context);
648- m_History.Record(op);
636+ if (recordHistory)
637+ {
638+ m_History.Record(op);
639+ }
640+ return op;
649641 }
650642
651- public void DeleteMWEAnnotation(List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList)
643+ public void PushHistories(IEnumerable<IOperation> ops)
652644 {
653- var op = new OperationDeleteMWEAnnotation(charRanges, dependencies, segLexList, linkLexList);
654- op.Execute(m_Context);
655- m_History.Record(op);
645+ foreach (var op in ops)
646+ {
647+ m_History.Record(op);
648+ }
656649 }
657650
651+ /// <summary>
652+ /// DBのTransaction Savepointを作る
653+ /// </summary>
654+ /// <param name="v"></param>
655+ public void CreateSavePoint(string spname)
656+ {
657+ m_Context.CreateSavePoint(spname);
658+ }
659+
660+ /// <summary>
661+ /// SavepointまでRollbackする
662+ /// </summary>
663+ /// <param name="v"></param>
664+ public void Unexecute(List<IOperation> ops)
665+ {
666+ for (var i = ops.Count - 1; i >= 0; i--)
667+ {
668+ (ops[i] as Operation).UnExecute(m_Context);
669+ }
670+ }
671+
672+ /// <summary>
673+ /// SavepointをReleaseする
674+ /// </summary>
675+ /// <param name="v"></param>
676+ public void ReleaseSavePoint(string spname)
677+ {
678+ try
679+ {
680+ m_Context.ReleaseSavePoint(spname);
681+ }
682+ catch (Exception ex)
683+ {
684+ }
685+ }
686+
658687 public void CreateLink(Segment fromseg, Segment toseg, string tagname)
659688 {
660689 var op = new OperationCreateLink(fromseg, toseg, tagname);
@@ -693,7 +722,7 @@
693722 public void UpdateAllBunsetsu(Sentence sen, string input)
694723 {
695724 // Cabocha出力を解析する
696- Operation op = new OperationUpdateAllBunsetsu(sen, input);
725+ var op = new OperationUpdateAllBunsetsu(sen, input);
697726 op.Execute(m_Context);
698727 m_History.Record(op);
699728 }
@@ -700,7 +729,7 @@
700729
701730 public bool Undo()
702731 {
703- Operation op = m_History.Back();
732+ var op = m_History.Back() as Operation;
704733 if (op != null)
705734 {
706735 op.UnExecute(m_Context);
@@ -711,7 +740,7 @@
711740
712741 public bool Redo()
713742 {
714- Operation op = m_History.Forward();
743+ var op = m_History.Forward() as Operation;
715744 if (op != null)
716745 {
717746 op.Execute(m_Context);
--- trunk/ChaKi.NET/src/Service/DependencyEdit/Operation.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/DependencyEdit/Operation.cs (revision 614)
@@ -11,10 +11,9 @@
1111
1212 namespace ChaKi.Service.DependencyEdit
1313 {
14- internal abstract class Operation
14+ internal abstract class Operation : IOperation
1515 {
1616 public abstract void Execute(DepEditContext ctx);
17-
1817 public abstract void UnExecute(DepEditContext ctx);
1918
2019 public virtual string ToIronRubyStatement(DepEditContext ctx) { return string.Empty; }
@@ -233,11 +232,16 @@
233232 public int NewToSegStartPos;
234233 public int NewToSegEndPos;
235234 public string NewToSegTagName;
235+ // Link Tag
236+ public string OldLinkTagName;
237+ public string NewLinkTagName;
236238
237239 public ChangeLinkInfo(int docid,
238240 int linkFromSegStartPos, int linkFromSegEndPos, string linkFromSegTagName,
239241 int newFromSegStartPos, int newFromSegEndPos, string newFromSegTagName,
240- int newToSegStartPos, int newToSegEndPos, string newToSegTagName)
242+ int newToSegStartPos, int newToSegEndPos, string newToSegTagName,
243+ string oldLinkTagName = null, string newLinkTagName = null
244+ )
241245 {
242246 this.DocId = docid;
243247 this.LinkFromSegStartPos = linkFromSegStartPos;
@@ -249,6 +253,8 @@
249253 this.NewToSegStartPos = newToSegStartPos;
250254 this.NewToSegEndPos = newToSegEndPos;
251255 this.NewToSegTagName = newToSegTagName;
256+ this.OldLinkTagName = oldLinkTagName;
257+ this.NewLinkTagName = newLinkTagName;
252258 }
253259 }
254260
--- trunk/ChaKi.NET/src/Service/DependencyEdit/History.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/DependencyEdit/History.cs (revision 614)
@@ -7,7 +7,7 @@
77 {
88 internal class History
99 {
10- private List<Operation> m_History;
10+ private List<IOperation> m_History;
1111 // HistPointer = 0: ヒストリの先頭までロールバックしている状態
1212 // = 1~m_Hisotry.Count-1: ヒストリの中間にいる(Undo/Redoどちらもできる)状態
1313 // = m_History.Count: ヒストリの最後にいる(最新の)状態
@@ -15,7 +15,7 @@
1515
1616 public History()
1717 {
18- m_History = new List<Operation>();
18+ m_History = new List<IOperation>();
1919 m_HistPointer = 0;
2020 }
2121
@@ -25,9 +25,9 @@
2525 m_HistPointer = 0;
2626 }
2727
28- public IList<Operation> GetCurrentOperationChain()
28+ public IList<IOperation> GetCurrentOperationChain()
2929 {
30- IList<Operation> ret = new List<Operation>();
30+ var ret = new List<IOperation>();
3131 for (int i = 0; i < m_HistPointer; i++)
3232 {
3333 ret.Add(m_History[i]);
@@ -35,7 +35,7 @@
3535 return ret;
3636 }
3737
38- public void Record(Operation op)
38+ public void Record(IOperation op)
3939 {
4040 if (m_History.Count - m_HistPointer > 0)
4141 {
@@ -46,7 +46,7 @@
4646 m_HistPointer = m_History.Count ;
4747 }
4848
49- public Operation Back()
49+ public IOperation Back()
5050 {
5151 if (m_HistPointer == 0 || m_HistPointer - 1 >= m_History.Count)
5252 {
@@ -56,13 +56,13 @@
5656 return m_History[m_HistPointer];
5757 }
5858
59- public Operation Forward()
59+ public IOperation Forward()
6060 {
6161 if (m_HistPointer >= m_History.Count)
6262 {
6363 return null;
6464 }
65- Operation op = m_History[m_HistPointer];
65+ var op = m_History[m_HistPointer];
6666 m_HistPointer++;
6767 return op;
6868 }
--- trunk/ChaKi.NET/src/Service/DependencyEdit/DictionaryAccessor_Cradle.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/DependencyEdit/DictionaryAccessor_Cradle.cs (revision 614)
@@ -94,22 +94,37 @@
9494 foreach (var item in jarray)
9595 {
9696 var mwe = new MWE();
97- var mwetoken = item as JArray;
98- foreach (var token in mwetoken)
97+ mwe.Lex = Json2Lexeme(item);
98+ var deps = item["Dependencies"] as JArray;
99+ foreach (var token in deps)
99100 {
100101 var mwenode = new MWENode();
101- mwenode.DependsTo = token["DepTo"].Value<int>();
102- JToken srcNodeLex = null;
102+ if (token["DepTo"] == null)
103+ {
104+ mwenode.DependsTo = -1;
105+ }
106+ else
107+ {
108+ mwenode.DependsTo = token["DepTo"].Value<int>();
109+ }
110+ if (token["DepAs"] == null)
111+ {
112+ mwenode.DependsAs = "";
113+ }
114+ else
115+ {
116+ mwenode.DependsAs = token["DepAs"].Value<string>();
117+ }
118+ JToken nodeLex = null;
103119 try
104120 {
105- srcNodeLex = token.SelectToken("SrcNode.Lex");
106- if (srcNodeLex != null)
121+ nodeLex = token["Lex"];
122+ if (nodeLex != null)
107123 {
108- mwenode.Label = ToSafeString(srcNodeLex["Surface"]);
109- mwenode.NodeType = (MWENodeType)srcNodeLex["NodeType"].Value<int>();
110- mwenode.SrcLex = Json2Lexeme(srcNodeLex);
124+ mwenode.Label = ToSafeString(nodeLex["Surface"]);
125+ mwenode.NodeType = (MWENodeType)nodeLex["NodeType"].Value<int>();
126+ mwenode.SrcLex = Json2Lexeme(nodeLex);
111127 }
112- mwenode.LinkLex = Json2Lexeme(token.SelectToken("LinkNode.Lex"));
113128 }
114129 catch (Exception ex)
115130 {
--- trunk/ChaKi.NET/src/Service/Database/OpContext.cs (revision 613)
+++ trunk/ChaKi.NET/src/Service/Database/OpContext.cs (revision 614)
@@ -5,6 +5,7 @@
55 using ChaKi.Entity.Corpora;
66 using ChaKi.Entity;
77 using System.Data;
8+using ChaKi.Service.DependencyEdit;
89
910 namespace ChaKi.Service.Database
1011 {
@@ -13,9 +14,9 @@
1314 /// ChaKi EntityのProject, Userも同時に保持し、DBへの変更が
1415 /// どのProject, Userによって行われているかを知っている.
1516 /// </summary>
16- public class OpContext : IDisposable
17+ public class OpContext : IContext, IDisposable
1718 {
18- protected OpContext()
19+ protected OpContext()
1920 {
2021 this.Session = null;
2122 this.Trans = null;
@@ -64,6 +65,37 @@
6465 this.Session.Delete(obj);
6566 }
6667
68+ public void CreateSavePoint(string spname)
69+ {
70+ using (var cmd = this.Session.Connection.CreateCommand())
71+ {
72+ cmd.CommandText = $"SAVEPOINT {spname}";
73+ cmd.CommandTimeout = 5; // 5 second
74+ cmd.ExecuteNonQuery();
75+ }
76+ }
77+
78+ public void ReleaseSavePoint(string spname)
79+ {
80+ using (var cmd = this.Session.Connection.CreateCommand())
81+ {
82+ cmd.CommandText = $"RELEASE SAVEPOINT {spname}";
83+ cmd.CommandTimeout = 5; // 5 second
84+ cmd.ExecuteNonQuery();
85+ }
86+ }
87+
88+ public void RollbackTo(string spname)
89+ {
90+ using (var cmd = this.Session.Connection.CreateCommand())
91+ {
92+ cmd.CommandText = $"ROLLBACK TO SAVEPOINT {spname}";
93+ cmd.CommandTimeout = 5; // 5 second
94+ cmd.ExecuteNonQuery();
95+ }
96+ }
97+
98+
6799 public void Flush()
68100 {
69101 this.Session.Flush();
--- trunk/ChaKi.NET/src/ImportWordRelation/Properties/AssemblyInfo.cs (revision 613)
+++ trunk/ChaKi.NET/src/ImportWordRelation/Properties/AssemblyInfo.cs (revision 614)
@@ -12,5 +12,5 @@
1212 [assembly: AssemblyCulture("")]
1313 [assembly: ComVisible(false)]
1414 [assembly: Guid("6a95808a-d1e3-47de-bb62-7ed7a281ac0b")]
15-[assembly: AssemblyVersion("3.11.612.0")]
16-[assembly: AssemblyFileVersion("3.11.612.0")]
15+[assembly: AssemblyVersion("3.11.613.0")]
16+[assembly: AssemblyFileVersion("3.11.613.0")]
--- trunk/ChaKi.NET/src/DepEditSLA/SentenceStructure.cs (revision 613)
+++ trunk/ChaKi.NET/src/DepEditSLA/SentenceStructure.cs (revision 614)
@@ -2704,6 +2704,8 @@
27042704
27052705 }
27062706
2707+ private List<IOperation> m_MweOps = new List<IOperation>();
2708+
27072709 public void DownloadMWE()
27082710 {
27092711 if (!CanAutoDetectMWE())
@@ -2712,9 +2714,11 @@
27122714 }
27132715 try
27142716 {
2717+ m_MweOps.Clear();
2718+
27152719 var words = this.Model.GetWords(this.TargetProjectId);
27162720 m_MWEDownloadSelector.Words = words;
2717- m_MWEDownloadSelector.OnApplyMatch += OnApplyMatch;
2721+ m_MWEDownloadSelector.OnApplyMweMatches += OnApplyMWEMatches;
27182722 m_MWEDownloadSelector.ShowDialog();
27192723 }
27202724 catch (Exception ex)
@@ -2724,11 +2728,22 @@
27242728 }
27252729 finally
27262730 {
2727- m_MWEDownloadSelector.OnApplyMatch -= OnApplyMatch;
2731+ m_MWEDownloadSelector.OnApplyMweMatches -= OnApplyMWEMatches;
27282732 }
2733+ try
2734+ {
2735+ // 現時点のLocal Execution HistoryをPushする.
2736+ m_Service.PushHistories(m_MweOps);
2737+ }
2738+ catch (Exception ex)
2739+ {
2740+ var edlg = new ErrorReportDialog("Command failed.", ex);
2741+ edlg.ShowDialog();
2742+ }
27292743 }
27302744
2731- private void OnApplyMatch(MatchingResult res, bool onoff)
2745+ // MWEアノテーションを一旦元に戻し、onされたMWEを順に生成しなおす.
2746+ private void OnApplyMWEMatches(MatchingResult[] matches, bool[] onoffs)
27322747 {
27332748 var words = m_MWEDownloadSelector.Words;
27342749 var oldCur = Cursor.Current;
@@ -2735,43 +2750,39 @@
27352750 Cursor.Current = Cursors.WaitCursor;
27362751 try
27372752 {
2738- // Word番号リストから文字位置範囲リストへ変換
2739- var charRanges = new List<CharRange>();
2740- var offs = this.Model.StartChar;
2741- foreach (var range in res.RangeList)
2753+ // チェックが変更されるたびにRollbackする
2754+ m_Service.Unexecute(m_MweOps);
2755+ m_MweOps.Clear();
2756+ for (var i = 0; i < matches.Length; i++)
27422757 {
2743- if (range.Start >= 0 && range.End >= 0)
2758+ var match = matches[i];
2759+ var onoff = onoffs[i];
2760+ if (onoff)
27442761 {
2745- charRanges.Add(new CharRange(words[range.Start].StartChar - offs, words[range.End - 1].EndChar - offs));
2762+ // Word番号リストから文字位置範囲リストへ変換
2763+ match.CalcCharRangeList(words, this.Model.StartChar);
2764+ var op = m_Service.CreateMWEAnnotation(match, false);
2765+ m_MweOps.Add(op);
27462766 }
27472767 }
2748- if (onoff)
2749- {
2750- m_Service.CreateMWEAnnotation(
2751- charRanges,
2752- res.DepList,
2753- (from item in res.MWE.Items select item.SrcLex).ToList(),
2754- (from item in res.MWE.Items select item.LinkLex).ToList()
2755- );
2756- }
2757- else
2758- {
2759- m_Service.DeleteMWEAnnotation(
2760- charRanges,
2761- res.DepList,
2762- (from item in res.MWE.Items select item.SrcLex).ToList(),
2763- (from item in res.MWE.Items select item.LinkLex).ToList()
2764- );
2765- }
2766- UpdateContents();
27672768 }
27682769 catch (Exception ex)
27692770 {
2771+ m_MweOps.Clear();
27702772 var edlg = new ErrorReportDialog("Command failed.", ex);
27712773 edlg.ShowDialog();
27722774 }
27732775 finally
27742776 {
2777+ try
2778+ {
2779+ UpdateContents();
2780+ }
2781+ catch (Exception ex)
2782+ {
2783+ var edlg = new ErrorReportDialog("Command failed.", ex);
2784+ edlg.ShowDialog();
2785+ }
27752786 Cursor.Current = oldCur;
27762787 }
27772788 }
--- trunk/ChaKi.NET/src/DepEditSLA/Widgets/MWEDownloadGrid.cs (revision 613)
+++ trunk/ChaKi.NET/src/DepEditSLA/Widgets/MWEDownloadGrid.cs (revision 614)
@@ -30,7 +30,7 @@
3030 public DataTable Table { get; set; }
3131 public List<Tuple<MWE, MatchingResult>> Results { get; set; }
3232
33- public event Action<MatchingResult, bool> OnApplyMatch;
33+ public event Action<MatchingResult[], bool[]> OnApplyMweMatches;
3434
3535 public MWEDownloadGrid(Corpus cps, ILexiconService lexsvc, IDepEditService depsvc)
3636 {
@@ -42,7 +42,6 @@
4242 this.Table.Columns.Add("Apply", typeof(bool));
4343 this.Table.Columns.Add("Surface", typeof(string));
4444 this.Table.Columns.Add("WordPositions", typeof(string));
45- this.Table.Columns.Add("Dependencies", typeof(string));
4645
4746 InitializeComponent();
4847
@@ -70,11 +69,12 @@
7069 {
7170 if (e.ColumnIndex == 0)
7271 {
73- var row = e.RowIndex;
74- var v = this.Table.Rows[row][0];
75- var @checked = (bool)(v);
72+ // mweリストを生成
73+ var mwes = from r in this.Results select r.Item2;
74+ // checkedリストを生成
75+ var checks = from r in this.Table.Rows.Cast<DataRow>() select (bool)r[0];
7676
77- this.OnApplyMatch(this.Results[row].Item2, @checked);
77+ this.OnApplyMweMatches(mwes.ToArray(), checks.ToArray());
7878 }
7979 }
8080
@@ -121,11 +121,10 @@
121121 private void AddMWE(MWE mwe, MatchingResult match)
122122 {
123123 BeginInvoke(new Action(() => {
124- this.Table.Rows.Add(
125- false,
126- MWEToString(mwe, this.Words, match),
127- string.Join(",", from r in match.RangeList select r.ToString()),
128- string.Join(",", match.DepList));
124+ this.Table.Rows.Add(
125+ false,
126+ MWEToString(mwe, this.Words, match),
127+ string.Join(",", from r in match.RangeList where (r.Start >= 0 && r.End >= 0) select r.ToString()));
129128 this.Results.Add(new Tuple<MWE, MatchingResult>(mwe, match));
130129 }));
131130 }
--- trunk/ChaKi.NET/src/ServiceInterface/DependencyEdit/IDepEditService.cs (revision 613)
+++ trunk/ChaKi.NET/src/ServiceInterface/DependencyEdit/IDepEditService.cs (revision 614)
@@ -4,6 +4,7 @@
44 using ChaKi.Entity.Corpora;
55 using ChaKi.Entity.Corpora.Annotations;
66 using System.IO;
7+using ChaKi.Common.SequenceMatcher;
78
89 namespace ChaKi.Service.DependencyEdit
910 {
@@ -155,10 +156,8 @@
155156
156157 bool HasInOutLink(Segment seg);
157158
158- void CreateMWEAnnotation(List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList);
159+ IOperation CreateMWEAnnotation(MatchingResult match, bool recordHistory);
159160
160- void DeleteMWEAnnotation(List<CharRange> charRanges, List<int> dependencies, List<Lexeme> segLexList, List<Lexeme> linkLexList);
161-
162161 void CreateLink(Segment fromseg, Segment toseg, string tagname);
163162
164163 void DeleteLink(Link link);
@@ -174,6 +173,11 @@
174173 /// </summary>
175174 void MergeWord(int docid, int bpos, int epos, int spos, string lexsurface);
176175
176+ /// <summary>
177+ /// Operationをまとめてヒストリに登録
178+ /// </summary>
179+ /// <param name="ops"></param>
180+ void PushHistories(IEnumerable<IOperation> ops);
177181
178182 bool Undo();
179183
@@ -230,6 +234,25 @@
230234 /// <param name="mwe"></param>
231235 /// <param name="deps"></param>
232236 void RegisterMWEToDictionary(MWE mwe);
237+
238+ /// <summary>
239+ /// DBのTransaction Savepointを作る
240+ /// </summary>
241+ /// <param name="v"></param>
242+ void CreateSavePoint(string spname);
243+
244+ /// <summary>
245+ /// opsを逆順にすべてUnExecute(Undo)する.
246+ /// </summary>
247+ /// <param name="v"></param>
248+ void Unexecute(List<IOperation> ops);
249+
250+ /// <summary>
251+ /// SavepointをReleaseする
252+ /// </summary>
253+ /// <param name="v"></param>
254+ void ReleaseSavePoint(string spname);
255+
233256 }
234257 }
235258
--- trunk/ChaKi.NET/src/ChaKiCommon/SequenceMatcher/MatchingResult.cs (revision 613)
+++ trunk/ChaKi.NET/src/ChaKiCommon/SequenceMatcher/MatchingResult.cs (revision 614)
@@ -11,6 +11,7 @@
1111 public List<IndexRange> RangeList { get; set; }
1212 public List<int> DepList { get; set; }
1313 public MWE MWE { get; set; }
14+ public List<CharRange> CharRangeList { get; set; }
1415
1516 public MatchingResult()
1617 {
@@ -17,5 +18,14 @@
1718 this.RangeList = new List<IndexRange>();
1819 this.DepList = new List<int>();
1920 }
21+
22+ public void CalcCharRangeList(IList<Word> words, int startChar)
23+ {
24+ this.CharRangeList =
25+ (from r in this.RangeList
26+ where (r.Start >= 0 && r.End >= 0)
27+ select new CharRange(words[r.Start].StartChar - startChar, words[r.End - 1].EndChar - startChar))
28+ .ToList();
29+ }
2030 }
2131 }
--- trunk/ChaKi.NET/src/ChaKiCommon/SequenceMatcher/MWEMatcher.cs (revision 613)
+++ trunk/ChaKi.NET/src/ChaKiCommon/SequenceMatcher/MWEMatcher.cs (revision 614)
@@ -4,6 +4,7 @@
44 using System.Diagnostics;
55 using System.Linq;
66 using System.Text;
7+using System.Text.RegularExpressions;
78
89 namespace ChaKi.Common.SequenceMatcher
910 {
@@ -63,6 +64,7 @@
6364 {
6465 continue;
6566 }
67+ n.Label = null; // PlaceholderのLabelはFindNexSubMatch()ではnullであることを前提としているため、"*"になっていても強制的にnullにする.
6668 var newranges = new List<IndexRange[]>();
6769 foreach (var q in ranges)
6870 {
@@ -116,8 +118,10 @@
116118 {
117119 var word = words[p];
118120 var lex = word.Lex;
121+ var pos = n.POS?.Replace("*", ".*");
119122 if ((string.IsNullOrEmpty(n.Label) || lex.Surface == n.Label)
120- && (string.IsNullOrEmpty(n.POS) || lex.PartOfSpeech.Name == n.POS)
123+ && (string.IsNullOrEmpty(n.POS) ||
124+ Regex.IsMatch(lex.PartOfSpeech.Name, pos))
121125 /* || lex.CType.Name != node_word.CType || lex.CForm.Name != node_word.CForm*/)
122126 {
123127 result.Add(new IndexRange(p));
--- trunk/ChaKi.NET/src/Text2Corpus/Properties/AssemblyInfo.cs (revision 613)
+++ trunk/ChaKi.NET/src/Text2Corpus/Properties/AssemblyInfo.cs (revision 614)
@@ -12,5 +12,5 @@
1212 [assembly: AssemblyCulture("")]
1313 [assembly: ComVisible(false)]
1414 [assembly: Guid("a8cf8403-eb88-418f-bf54-56aeaef39268")]
15-[assembly: AssemblyVersion("3.11.612.0")]
16-[assembly: AssemblyFileVersion("3.11.612.0")]
15+[assembly: AssemblyVersion("3.11.613.0")]
16+[assembly: AssemblyFileVersion("3.11.613.0")]
--- trunk/ChaKi.NET/src/Text2Corpus/doc/ChaMameHelp.html (revision 613)
+++ trunk/ChaKi.NET/src/Text2Corpus/doc/ChaMameHelp.html (revision 614)
@@ -368,7 +368,7 @@
368368 </style>
369369 </head>
370370 <body class='markdown-preview' data-use-github-style><h1 id="chamame-">ChaMame ユーザーズマニュアル</h1>
371-<p style="text-align:right">2018.3.9 Version 1.0</p>
371+<p style="text-align:right">2018.3.26 Version 1.0</p>
372372
373373 <h2 id="-">概要</h2>
374374 <p>平文形式のテキストファイルを、MeCab形式やCaboCha形式に変換するGUIツールです。
@@ -427,11 +427,11 @@
427427 <p><code>default</code>を選ぶと、<code>-O</code>オプションを付加しません。</p>
428428 <h4 id="-">付加カラム</h4>
429429 <p>分類語彙表に従って、形態素解析された単語それぞれが分類される語彙IDやラベルをMecabの第3カラム(Tab区切りで)に出力します。分類語彙表データは、ChaMameインストールフォルダの下の<code>BunruiNo_LemmaID.txt</code>にあります。</p>
430-<p>このオプションを利用する(付加カラムなし以外を指定する)ためには、Mecab出力の最終カラム(CSV区切りで)がLemmaIDとなっている必要があります。(<code>unidic22</code>出力形式など)<br>
431-また、次の文節・係り受け解析をONにした場合は、付加カラムはcabochaの出力に付加されます。</p>
430+<p>このオプションを利用する(付加カラムなし以外を指定する)ためには、Mecab出力の最終カラム(CSV区切りで)がLemmaIDとなっている必要があります。(<code>unidic22</code>出力形式など)</p>
432431 <h3 id="-cabocha-">文節・係り受け解析(Cabocha)オプションを指定する</h3>
433-<p>文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合は出力は<code>.mecab</code>と<code>.cabocha</code>の両方となります。</p>
434-<p>別途Cabochaがインストールされていることが必要です。</p>
432+<p>文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合の出力は<code>.cabocha</code>ファイルになります。</p>
433+<p>別途Cabochaがインストールされていることが必要です。
434+また、形態素解析のオプションで、付加カラムが「なし」以外の場合はCabochaの出力ができません。</p>
435435 <h3 id="-">処理を実行する</h3>
436436 <p>以上の設定を行ったうえで、<strong>実行</strong> ボタンをクリックすると処理が開始されます。</p>
437437 <p>解析結果は、<strong>出力先</strong> 欄に示されるフォルダ(変換元パスに<code>/out</code>を付加したフォルダ。なければ生成します)に出力されます。<strong>フォルダ表示</strong> ボタンでフォルダの内容がExplorerによって表示されます。出力先の<code>out</code>フォルダは設定によって変更することはできません。</p>
--- trunk/ChaKi.NET/src/Text2Corpus/doc/chamame.md (revision 613)
+++ trunk/ChaKi.NET/src/Text2Corpus/doc/chamame.md (revision 614)
@@ -1,5 +1,5 @@
11 # ChaMame ユーザーズマニュアル
2-<P style="text-align:right">2018.3.9 Version 1.0</P>
2+<P style="text-align:right">2018.3.26 Version 1.0</P>
33
44 ## 概要
55
@@ -74,7 +74,7 @@
7474 また、次の文節・係り受け解析をONにした場合は、付加カラムはcabochaの出力に付加されます。
7575
7676 ### 文節・係り受け解析(Cabocha)オプションを指定する
77-文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合は出力は`.mecab`と`.cabocha`の両方となります。
77+文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合の出力は`.cabocha`ファイルになります。
7878
7979 別途Cabochaがインストールされていることが必要です。
8080
--- trunk/ChaKi.NET/src/Text2Corpus/ChaMameHelp.html (revision 613)
+++ trunk/ChaKi.NET/src/Text2Corpus/ChaMameHelp.html (revision 614)
@@ -368,7 +368,7 @@
368368 </style>
369369 </head>
370370 <body class='markdown-preview' data-use-github-style><h1 id="chamame-">ChaMame ユーザーズマニュアル</h1>
371-<p style="text-align:right">2018.3.9 Version 1.0</p>
371+<p style="text-align:right">2018.3.26 Version 1.0</p>
372372
373373 <h2 id="-">概要</h2>
374374 <p>平文形式のテキストファイルを、MeCab形式やCaboCha形式に変換するGUIツールです。
@@ -429,7 +429,7 @@
429429 <p>分類語彙表に従って、形態素解析された単語それぞれが分類される語彙IDやラベルをMecabの第3カラム(Tab区切りで)に出力します。分類語彙表データは、ChaMameインストールフォルダの下の<code>BunruiNo_LemmaID.txt</code>にあります。</p>
430430 <p>このオプションを利用する(付加カラムなし以外を指定する)ためには、Mecab出力の最終カラム(CSV区切りで)がLemmaIDとなっている必要があります。(<code>unidic22</code>出力形式など)</p>
431431 <h3 id="-cabocha-">文節・係り受け解析(Cabocha)オプションを指定する</h3>
432-<p>文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合は出力は<code>.mecab</code>と<code>.cabocha</code>の両方となります。</p>
432+<p>文節・係り受け解析チェックボックスをONにすると、Mecab出力をそのままCabocha解析器に渡します。この場合の出力は<code>.cabocha</code>ファイルになります。</p>
433433 <p>別途Cabochaがインストールされていることが必要です。
434434 また、形態素解析のオプションで、付加カラムが「なし」以外の場合はCabochaの出力ができません。</p>
435435 <h3 id="-">処理を実行する</h3>
Show on old repository browser