source: trunk/CrypPlugins/WordPatterns/WordPatterns.cs @ 2061

Last change on this file since 2061 was 962, checked in by Matthäus Wander, 12 years ago

AnotherEditor:

  • does not show a warning anymore when a missing property in save file is marked with the DontSave attribute

CrypWin:

  • changed AssemblyCompany to "CrypTool2" as this string is being used to determine AppData directory (where the user.config file is located)
  • added support for arbitrary enums as TaskPane settings (see WordPatterns how to use them)

Dictionary:

  • fixed some bugs in Dictionary file handling

WordPatterns:

  • added XAML description
  • added case sensitivity setting (enum!)
  • minor sample fix

Note: New binaries are not included as I have some more unfinished core changes on my machine.

File size: 10.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using Cryptool.PluginBase;
6using Cryptool.PluginBase.Analysis;
7using Cryptool.PluginBase.IO;
8using Cryptool.PluginBase.Miscellaneous;
9using System.ComponentModel;
10using System.Diagnostics;
11
12namespace WordPatterns
13{
14    /*
15     * Proposed changes and enhancements:
16     * - multiple word search with one TextInput (split words at whitespace)
17     * - enter max match number
18     * - enter pattern in number format (like 1-2-2-1)
19     * - add filter function (see Borland C++ tool)
20     * - save last input words and propose them to user
21     * - improve performance
22     * - support wildcard (*)
23     */
24    [Author("Matthäus Wander", "wander@cryptool.org", "Fachgebiet Verteilte Systeme, Universität Duisburg-Essen", "http://www.vs.uni-due.de")]
25    [PluginInfo(false, "WordPatterns", "Searches for words with the same pattern", "WordPatterns/WordPatternsDescription.xaml", "CrypWin/images/default.png")]
26    public class WordPatterns : IAnalysisMisc
27    {
28        #region Private stuff
29
30        private WordPatternsSettings settings = new WordPatternsSettings();
31
32        private string inputText;
33        private string[] inputDict;
34        private string outputText;
35
36        private IDictionary<Pattern, IList<string>> dictPatterns;
37       
38        private bool stop = false;
39
40        #endregion
41
42        #region Properties
43
44        [PropertyInfo(Direction.InputData, "Input word", "Word to search for patterns", "", true, false, DisplayLevel.Beginner, QuickWatchFormat.Text, null)]
45        public string InputText
46        {
47            get
48            {
49                return inputText;
50            }
51            set
52            {
53                inputText = value;
54                OnPropertyChanged("InputText");
55            }
56        }
57
58        [PropertyInfo(Direction.InputData, "Input dictionary", "Word dictionary", "", true, false, DisplayLevel.Beginner, QuickWatchFormat.Text, null)]
59        public string[] InputDict
60        {
61            get
62            {
63                return inputDict;
64            }
65            set
66            {
67                inputDict = value;
68                dictPatterns = null; // force rebuild of dictionary patterns
69                OnPropertyChanged("InputDict");
70            }
71        }
72
73        [PropertyInfo(Direction.OutputData, "Output words", "Words matching the pattern", "", false, false, DisplayLevel.Beginner, QuickWatchFormat.Text, null)]
74        public string OutputText
75        {
76            get { return outputText; }
77            private set
78            {
79                outputText = value;
80                OnPropertyChanged("OutputText");
81            }
82        }
83
84        public bool CaseSensitive
85        {
86            get
87            {
88                return settings.CaseSelection == Case.Sensitive;
89            }
90        }
91
92        #endregion
93
94        #region IPlugin Members
95
96        public event StatusChangedEventHandler OnPluginStatusChanged;
97
98        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
99        private void GuiLogMessage(string p, NotificationLevel notificationLevel)
100        {
101            EventsHelper.GuiLogMessage(OnGuiLogNotificationOccured, this, new GuiLogEventArgs(p, this, notificationLevel));
102        }
103
104        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
105        private void ProgressChanged(double value, double max)
106        {
107            EventsHelper.ProgressChanged(OnPluginProgressChanged, this, new PluginProgressEventArgs(value, max));
108        }
109
110        public ISettings Settings
111        {
112            get { return settings; }
113            set { settings = (WordPatternsSettings) value; }
114        }
115
116        public System.Windows.Controls.UserControl Presentation
117        {
118            get { return null; }
119        }
120
121        public System.Windows.Controls.UserControl QuickWatchPresentation
122        {
123            get { return null; }
124        }
125
126        public void PreExecution()
127        {
128            stop = false;
129        }
130
131        public void Execute()
132        {
133            if (inputText == null)
134            {
135                OutputText = "";
136                return;
137            }
138
139            // calculate input word pattern
140            Pattern inputPattern = new Pattern(inputText, CaseSensitive);
141
142            if (inputDict == null)
143                return;
144
145            // If not already done, calculate pattern for each dictionary word
146            if (dictPatterns == null)
147            {
148                dictPatterns = new Dictionary<Pattern, IList<string>>();
149                int wordCount = 0;
150
151                while (wordCount < inputDict.Length && !stop)
152                {
153                    string word = inputDict[wordCount];
154                    Pattern p = new Pattern(word, CaseSensitive);
155
156                    // two calls to Pattern.GetHashCode()
157                    if (!dictPatterns.ContainsKey(p))
158                        dictPatterns[p] = new List<string>();
159
160                    // one call to Pattern.GetHashCode() and one to Pattern.Equals()
161                    dictPatterns[p].Add(word);
162
163                    if (++wordCount % 10000 == 0)
164                    {
165                        ProgressChanged(wordCount, inputDict.Length);
166                    }
167                }
168
169                ProgressChanged(wordCount, inputDict.Length);
170                GuiLogMessage(string.Format("Processed {0} words from dictionary.", wordCount), NotificationLevel.Info);
171            }
172
173            // retrieve words matching input pattern
174            if (dictPatterns.ContainsKey(inputPattern))
175            {
176                StringBuilder sb = new StringBuilder();
177                IList<string> matches = dictPatterns[inputPattern];
178                foreach (string word in matches)
179                {
180                    sb.Append(word);
181                    sb.AppendLine();
182                }
183                OutputText = sb.ToString();
184            }
185            else
186            {
187                OutputText = "";
188            }
189        }
190
191        internal struct Pattern
192        {
193            private const int PRIME = 16777619;
194
195            private readonly int[] patternArray;
196            private readonly int hashCode;
197
198            internal Pattern(string word, bool caseSensitive)
199            {
200                if (!caseSensitive)
201                    word = word.ToLower();
202
203                patternArray = new int[word.Length];
204                hashCode = -2128831035; // int32 counterpart of uint32 2166136261
205               
206                Dictionary<char, int> seenLetters = new Dictionary<char, int>(15);
207                int letterNumber = 0;
208               
209                for (int i = 0; i < word.Length; i++)
210                {
211                    if (seenLetters.ContainsKey(word[i])) // letter already seen?
212                    {
213                        patternArray[i] = seenLetters[word[i]]; // get letter number
214                    }
215                    else
216                    {
217                        seenLetters[word[i]] = patternArray[i] = ++letterNumber; // create new letter number
218                    }
219
220                    // FNV-1 hashing
221                    hashCode = (hashCode * PRIME) ^ patternArray[i];
222                }
223
224                seenLetters = null;
225            }
226
227            /// <summary>
228            /// Returns pre-calculated hash code.
229            /// </summary>
230            /// <returns></returns>
231            public override int GetHashCode()
232            {
233                return hashCode;
234            }
235
236            /// <summary>
237            /// In-depth comparison of pattern array contents.
238            /// </summary>
239            /// <param name="obj"></param>
240            /// <returns></returns>
241            public override bool Equals(object right)
242            {
243                if (right == null)
244                    return false;
245
246                // Never true for value types
247                //if (object.ReferenceEquals(this, right))
248                //    return true;
249
250                // Using the as/is operators can break symmetry requirement for reference types.
251                // However this does not apply for value types.
252                //if (this.GetType() != right.GetType())
253                //    return false;
254                if (!(right is Pattern))
255                    return false;
256
257                return this == (Pattern)right;
258            }
259
260            public static bool operator==(Pattern left, Pattern right)
261            {
262                if (left.hashCode != right.hashCode)
263                    return false;
264
265                if (left.patternArray.Length != right.patternArray.Length)
266                    return false;
267
268                for (int i = 0; i < left.patternArray.Length; i++)
269                {
270                    // uneven pattern content
271                    if (left.patternArray[i] != right.patternArray[i])
272                        return false;
273                }
274
275                return true;
276            }
277
278            public static bool operator !=(Pattern left, Pattern right)
279            {
280                return !(left == right);
281            }
282        }
283
284        /// <summary>
285        /// equals to (int) Math.pow(10, x), but does not require type casting between double and int
286        /// </summary>
287        /// <param name="x"></param>
288        /// <returns></returns>
289        public static int power10(int x)
290        {
291            int result = 1;
292            for (int i = 0; i < x; i++)
293            {
294                result *= 10;
295            }
296            return result;
297        }
298
299        public void PostExecution()
300        {
301            GuiLogMessage("PostExecution has been called. Cleaning pattern dictionary...", NotificationLevel.Info);
302            dictPatterns = null;
303        }
304
305        public void Pause()
306        {
307        }
308
309        public void Stop()
310        {
311            stop = true;
312        }
313
314        public void Initialize()
315        {
316        }
317
318        public void Dispose()
319        {
320        }
321
322        #endregion
323
324        #region INotifyPropertyChanged Members
325
326        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
327        private void OnPropertyChanged(string p)
328        {
329            EventsHelper.PropertyChanged(PropertyChanged, this, new PropertyChangedEventArgs(p));
330        }
331
332        #endregion
333
334    }
335}
Note: See TracBrowser for help on using the repository browser.