/// <summary> /// Attempts to retrieve the kanji strokes SVG matching the given kanji inside the zip file. /// </summary> /// <param name="k">Target kanji.</param> /// <returns>A kanji strokes entity, never null, that contains either the retrieved data or an /// empty byte array when the entry was not found.</returns> private KanjiStrokes RetrieveSvg(KanjiEntity k) { KanjiStrokes strokes = new KanjiStrokes(); strokes.FramesSvg = new byte[0]; if (!k.UnicodeValue.HasValue) { return(strokes); } ZipArchive svgZip = GetSvgZipArchive(); string entryName = string.Format("{0}_frames.svg", k.UnicodeValue.Value); ZipArchiveEntry entry = svgZip.GetEntry(entryName); if (entry != null) { using (Stream stream = entry.Open()) { strokes.FramesSvg = StringCompressionHelper.Zip(StreamHelper.ReadToEnd(stream)); StringCompressionHelper.Unzip(strokes.FramesSvg); } } return(strokes); }
/// <summary> /// Gets all kanji with minimal info. /// </summary> /// <returns>All kanji with minimal info.</returns> public IEnumerable <KanjiEntity> GetAllKanji() { DaoConnection connection = null; try { // Create and open synchronously the primary Kanji connection. connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase); IEnumerable <NameValueCollection> results = connection.Query( string.Format("SELECT * FROM {0}", SqlHelper.Table_Kanji)); KanjiBuilder kanjiBuilder = new KanjiBuilder(); foreach (NameValueCollection nvcKanji in results) { KanjiEntity kanji = kanjiBuilder.BuildEntity(nvcKanji, null); yield return(kanji); } } finally { if (connection != null) { connection.Dispose(); } } }
/// <summary> /// Loads properties from the given kanji entity. /// </summary> /// <param name="se">Target SRS entry.</param> /// <param name="k">Kanji to load.</param> public static void LoadFromKanji(this SrsEntry se, KanjiEntity k) { // Compute the meaning string. string meaningString = string.Empty; foreach (KanjiMeaning km in k.Meanings) { meaningString += MultiValueFieldHelper.ReplaceSeparator(km.Meaning) + MultiValueFieldHelper.ValueSeparator; } meaningString = meaningString.Trim( new char[] { MultiValueFieldHelper.ValueSeparator }); meaningString = MultiValueFieldHelper.Expand(meaningString); // Compute the reading string. string readingString = (k.OnYomi + MultiValueFieldHelper.ValueSeparator) + k.KunYomi; readingString = MultiValueFieldHelper.Expand(readingString .Trim(new char[] { MultiValueFieldHelper.ValueSeparator })); // Set values. se.Meanings = meaningString; se.Readings = readingString; se.AssociatedKanji = k.Character; }
public VocabFilter() { Kanji = new KanjiEntity[0] { }; IsCommonFirst = true; IsShortReadingFirst = true; JlptLevel = Levels.IgnoreJlptLevel; }
/// <summary> /// Retrieves and returns the collection of vocab matching the /// given filters. /// </summary> /// <param name="kanji">Kanji filter. Only vocab containing this /// kanji will be filtered in.</param> /// <param name="readingFilter">Reading filter. Only vocab containing /// this string in their kana or kanji reading will be filtered in.</param> /// <param name="meaningFilter">Meaning filter. Only vocab containing /// this string as part of at least one of their meaning entries will /// be filtered in.</param> /// <param name="categoryFilter">If not null, this category is used as the filter.</param> /// <param name="jlptLevel">The JLPT level to filter /// (1-5, where a lower value means it is not covered on the JLPT /// and a higher value means that this filter will be ignored).</param> /// <param name="wkLevel">The WaniKani level to filter /// (1-60, where a higher value means it is not taught by WaniKani /// and a lower value means that this filter will be ignored).</param> /// <param name="isCommonFirst">Indicates if common vocab should be /// presented first. If false, results are sorted only by the length /// of their writing (asc or desc depending on the parameter)</param> /// <param name="isShortWritingFirst">Indicates if results should /// be sorted by ascending or descending writing length. /// If True, short readings come first. If False, long readings /// come first.</param> /// <returns>Vocab entities matching the filters.</returns> public IEnumerable <VocabEntity> GetFilteredVocab(KanjiEntity kanji, string readingFilter, string meaningFilter, VocabCategory categoryFilter, int jlptLevel, int wkLevel, bool isCommonFirst, bool isShortWritingFirst) { List <DaoParameter> parameters = new List <DaoParameter>(); string sqlFilterClauses = BuildVocabFilterClauses(parameters, kanji, readingFilter, meaningFilter, categoryFilter, jlptLevel, wkLevel); string sortClause = "ORDER BY "; if (isCommonFirst) { sortClause += string.Format("v.{0} DESC,", SqlHelper.Field_Vocab_IsCommon); } sortClause += string.Format("length(v.{0}) {1}", SqlHelper.Field_Vocab_KanaWriting, (isShortWritingFirst ? "ASC" : "DESC")); DaoConnection connection = null; DaoConnection srsConnection = null; try { connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase); srsConnection = new DaoConnection(DaoConnectionEnum.SrsDatabase); srsConnection.OpenAsync(); IEnumerable <NameValueCollection> vocabs = connection.Query( string.Format("SELECT DISTINCT v.* FROM {0} v {1}{2}", SqlHelper.Table_Vocab, sqlFilterClauses, sortClause), parameters.ToArray()); VocabBuilder vocabBuilder = new VocabBuilder(); foreach (NameValueCollection nvcVocab in vocabs) { VocabEntity vocab = vocabBuilder.BuildEntity(nvcVocab, null); IncludeCategories(connection, vocab); IncludeMeanings(connection, vocab); IncludeKanji(connection, srsConnection, vocab); IncludeSrsEntries(srsConnection, vocab); IncludeVariants(connection, vocab); yield return(vocab); } } finally { if (connection != null) { connection.Dispose(); } } }
/// <summary> /// Gets a set of kanji matching the given filters. /// </summary> /// <param name="radicals">Filters out kanji which do not contain all /// of the contained radicals.</param> /// <param name="textFilter">If set, filters out all kanji that are not /// contained in the string.</param> /// <param name="meaningFilter">Filter for the meaning of the kanji.</param> /// <param name="anyReadingFilter">Filter matching any reading of the kanji. /// <remarks>If set, this parameter will override the three reading filters. /// </remarks></param> /// <param name="onYomiFilter">Filter for the on'yomi reading of the kanji. /// <remarks>This parameter will be ignored if /// <paramref name="anyReadingFilter"/> is set.</remarks></param> /// <param name="kunYomiFilter">Filter for the kun'yomi reading of the kanji. /// <remarks>This parameter will be ignored if /// <paramref name="anyReadingFilter"/> is set.</remarks></param> /// <param name="nanoriFilter">Filter for the nanori reading of the kanji. /// <remarks>This parameter will be ignored if /// <paramref name="anyReadingFilter"/> is set.</remarks></param> /// <returns>Kanji matching the given filters.</returns> public IEnumerable <KanjiEntity> GetFilteredKanji(RadicalGroup[] radicals, string textFilter, string meaningFilter, string anyReadingFilter, string onYomiFilter, string kunYomiFilter, string nanoriFilter, int jlptLevel, int wkLevel) { List <DaoParameter> parameters = new List <DaoParameter>(); string sqlFilter = BuildKanjiFilterClauses(parameters, radicals, textFilter, meaningFilter, anyReadingFilter, onYomiFilter, kunYomiFilter, nanoriFilter, jlptLevel, wkLevel); DaoConnection connection = null; DaoConnection srsConnection = null; try { // Create and open synchronously the primary Kanji connection. connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase); // Create the secondary Srs connection and open it asynchronously. srsConnection = new DaoConnection(DaoConnectionEnum.SrsDatabase); srsConnection.OpenAsync(); // FILTERS COMPUTED. // Execute the final request. IEnumerable <NameValueCollection> results = connection.Query(string.Format( "SELECT * FROM {0} k {1}ORDER BY (k.{2} IS NULL),(k.{2});", SqlHelper.Table_Kanji, sqlFilter, SqlHelper.Field_Kanji_MostUsedRank), parameters.ToArray()); KanjiBuilder kanjiBuilder = new KanjiBuilder(); foreach (NameValueCollection nvcKanji in results) { KanjiEntity kanji = kanjiBuilder.BuildEntity(nvcKanji, null); IncludeKanjiMeanings(connection, kanji); IncludeRadicals(connection, kanji); IncludeSrsEntries(srsConnection, kanji); yield return(kanji); } } finally { if (connection != null) { connection.Dispose(); } if (srsConnection != null) { srsConnection.Dispose(); } } }
/// <summary> /// Retrieves and includes the SRS entries matching the given kanji and includes /// them in the entity. /// </summary> internal static void IncludeSrsEntries(DaoConnection connection, KanjiEntity kanji) { IEnumerable <NameValueCollection> nvcEntries = connection.Query( string.Format("SELECT * FROM {0} srs WHERE srs.{1}=@k", SqlHelper.Table_SrsEntry, SqlHelper.Field_SrsEntry_AssociatedKanji), new DaoParameter("@k", kanji.Character)); SrsEntryBuilder srsEntryBuilder = new SrsEntryBuilder(); foreach (NameValueCollection nvcEntry in nvcEntries) { kanji.SrsEntries.Add(srsEntryBuilder.BuildEntity(nvcEntry, null)); } }
/// <summary> /// See <see cref="Kanji.Database.Dao.VocabDao.GetFilteredVocab"/>. /// Returns the results count. /// </summary> public long GetFilteredVocabCount(KanjiEntity kanji, string readingFilter, string meaningFilter, VocabCategory categoryFilter, int jlptLevel, int wkLevel) { List <DaoParameter> parameters = new List <DaoParameter>(); string sqlFilterClauses = BuildVocabFilterClauses(parameters, kanji, readingFilter, meaningFilter, categoryFilter, jlptLevel, wkLevel); using (DaoConnection connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase)) { return((long)connection.QueryScalar( string.Format("SELECT count(1) FROM {0} v {1}", SqlHelper.Table_Vocab, sqlFilterClauses), parameters.ToArray())); } }
/// <summary> /// Retrieves and includes the meanings of the given kanji in the entity. /// </summary> internal static void IncludeKanjiMeanings(DaoConnection connection, KanjiEntity kanji) { IEnumerable <NameValueCollection> nvcMeanings = connection.Query( string.Format("SELECT * FROM {0} km WHERE km.{1}=@kanjiId AND km.{2} IS NULL;", SqlHelper.Table_KanjiMeaning, SqlHelper.Field_KanjiMeaning_KanjiId, SqlHelper.Field_KanjiMeaning_Language), new DaoParameter("@kanjiId", kanji.ID)); KanjiMeaningBuilder meaningBuilder = new KanjiMeaningBuilder(); foreach (NameValueCollection nvcMeaning in nvcMeanings) { // For each meaning result : build a meaning and set the associations. KanjiMeaning meaning = meaningBuilder.BuildEntity(nvcMeaning, null); meaning.Kanji = kanji; kanji.Meanings.Add(meaning); } }
/// <summary> /// Retrieves and includes kanji entities in the given radical entity. /// </summary> private void IncludeKanji(DaoConnection connection, RadicalEntity radical) { IEnumerable <NameValueCollection> results = connection.Query( string.Format( "SELECT kr.{0} {1} FROM {2} kr WHERE kr.{3}=@rid", SqlHelper.Field_Kanji_Radical_KanjiId, SqlHelper.Field_Kanji_Id, SqlHelper.Table_Kanji_Radical, SqlHelper.Field_Kanji_Radical_RadicalId), new DaoParameter("@rid", radical.ID)); KanjiBuilder kanjiBuilder = new KanjiBuilder(); foreach (NameValueCollection nvcKanjiRadical in results) { KanjiEntity kanji = kanjiBuilder.BuildEntity(nvcKanjiRadical, null); radical.Kanji.Add(kanji); } }
/// <summary> /// Retrieves and includes the radicals of the given kanji in the entity. /// </summary> internal static void IncludeRadicals(DaoConnection connection, KanjiEntity kanji) { IEnumerable <NameValueCollection> nvcRadicals = connection.Query( string.Format("SELECT * FROM {0} r JOIN {1} kr ON (kr.{2}=r.{3}) WHERE kr.{4}=@kanjiId;", SqlHelper.Table_Radical, SqlHelper.Table_Kanji_Radical, SqlHelper.Field_Kanji_Radical_RadicalId, SqlHelper.Field_Radical_Id, SqlHelper.Field_Kanji_Radical_KanjiId), new DaoParameter("@kanjiId", kanji.ID)); RadicalBuilder radicalBuilder = new RadicalBuilder(); foreach (NameValueCollection nvcRadical in nvcRadicals) { // For each meaning result : build a radical and set the associations. RadicalEntity radical = radicalBuilder.BuildEntity(nvcRadical, null); kanji.Radicals.Add(radical); } }
/// <summary> /// Includes the kanji of the given vocab in the entity. /// </summary> private void IncludeKanji(DaoConnection connection, DaoConnection srsConnection, VocabEntity vocab) { IEnumerable <NameValueCollection> results = connection.Query( string.Format("SELECT k.* FROM {0} kv JOIN {1} k ON (k.{2}=kv.{3}) WHERE kv.{4}=@vid", SqlHelper.Table_Kanji_Vocab, SqlHelper.Table_Kanji, SqlHelper.Field_Kanji_Id, SqlHelper.Field_Kanji_Vocab_KanjiId, SqlHelper.Field_Kanji_Vocab_VocabId), new DaoParameter("@vid", vocab.ID)); KanjiBuilder kanjiBuilder = new KanjiBuilder(); foreach (NameValueCollection nvcKanji in results) { KanjiEntity kanji = kanjiBuilder.BuildEntity(nvcKanji, null); KanjiDao.IncludeKanjiMeanings(connection, kanji); KanjiDao.IncludeRadicals(connection, kanji); KanjiDao.IncludeSrsEntries(srsConnection, kanji); vocab.Kanji.Add(kanji); } }
/// <summary> /// Gets the first kanji that matches the given character. /// </summary> /// <param name="character">Character to match.</param> /// <returns>First kanji matching the given character. /// Null if nothing was found.</returns> public KanjiEntity GetFirstMatchingKanji(string character) { KanjiEntity result = null; DaoConnection connection = null; try { // Create and open synchronously the primary Kanji connection. connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase); // FILTERS COMPUTED. // Execute the final request. IEnumerable <NameValueCollection> results = connection.Query(string.Format( "SELECT * FROM {0} k WHERE k.{1}=@k ORDER BY (k.{2} IS NULL),(k.{2});", SqlHelper.Table_Kanji, SqlHelper.Field_Kanji_Character, SqlHelper.Field_Kanji_MostUsedRank), new DaoParameter("@k", character)); if (results.Any()) { result = new KanjiBuilder() .BuildEntity(results.First(), null); IncludeKanjiMeanings(connection, result); } } finally { if (connection != null) { connection.Dispose(); } } return(result); }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) { return(null); } if (value is KanjiEntity && parameter is KanjiToStringConversionType) { KanjiEntity kanji = ((KanjiEntity)value); KanjiToStringConversionType conversionType = (KanjiToStringConversionType)parameter; KanjiMeaning[] meanings = kanji.Meanings.ToArray(); if (meanings.Any()) { if (conversionType == KanjiToStringConversionType.Short) { // Short conversion // Take the first meaning KanjiMeaning meaning = meanings.First(); if (!string.IsNullOrEmpty(meaning.Meaning)) { // Capitalize the first letter string capitalizedFirstLetter = meaning.Meaning.Substring(0, 1).ToUpper(); string meaningString = capitalizedFirstLetter + meaning.Meaning.Substring(1); // Shorten the result if too long if (meaningString.Length > MaxStringLength) { meaningString = meaningString.Substring(0, MaxStringLength) + "[...]"; } // Return the formatted first meaning return(meaningString); } } else { // Full conversion // Build a string containing each meaning separated by a comma. StringBuilder fullMeaningBuilder = new StringBuilder(); foreach (KanjiMeaning meaning in meanings) { fullMeaningBuilder.Append(meaning.Meaning); if (meaning != meanings.Last()) { fullMeaningBuilder.Append(", "); } } // Return the formatted meanings string. return(fullMeaningBuilder.ToString()); } } } else { // Invalid arguments. throw new ArgumentException( "This converter needs a KanjiEntity as a value and a ConversionType as a parameter."); } // There are no meanings to display. return("(No meaning)"); }
public ExtendedKanji(KanjiEntity dbKanji) { DbKanji = dbKanji; RadicalStore.Instance.IssueWhenLoaded(OnRadicalsLoaded); }
/// <summary> /// Builds and returns the vocab filter SQL clauses from the given /// filters. /// </summary> internal string BuildVocabFilterClauses(List <DaoParameter> parameters, KanjiEntity kanji, string readingFilter, string meaningFilter, VocabCategory categoryFilter, int jlptLevel, int wkLevel) { const int minJlptLevel = Levels.MinJlptLevel; const int maxJlptLevel = Levels.MaxJlptLevel; const int minWkLevel = Levels.MinWkLevel; const int maxWkLevel = Levels.MaxWkLevel; string sqlJlptFilter = string.Empty; if (jlptLevel >= minJlptLevel && jlptLevel <= maxJlptLevel) { sqlJlptFilter = string.Format("v.{0}=@jlpt ", SqlHelper.Field_Vocab_JlptLevel); parameters.Add(new DaoParameter("@jlpt", jlptLevel)); } else if (jlptLevel < minJlptLevel) { sqlJlptFilter = string.Format("v.{0} IS NULL ", SqlHelper.Field_Vocab_JlptLevel); } string sqlWkFilter = string.Empty; if (wkLevel >= minWkLevel && wkLevel <= maxWkLevel) { sqlWkFilter = string.Format("v.{0}=@wk ", SqlHelper.Field_Vocab_WaniKaniLevel); parameters.Add(new DaoParameter("@wk", wkLevel)); } else if (wkLevel > maxWkLevel) { sqlWkFilter = string.Format("v.{0} IS NULL ", SqlHelper.Field_Vocab_WaniKaniLevel); } string sqlKanjiFilter = string.Empty; if (kanji != null) { // Build the sql kanji filter clause. // Example with the kanji '達' : // // WHERE v.KanjiWriting LIKE '%達%' sqlKanjiFilter = string.Format("v.{0} LIKE @kanji ", SqlHelper.Field_Vocab_KanjiWriting); parameters.Add(new DaoParameter("@kanji", "%" + kanji.Character + "%")); } string sqlReadingFilter = string.Empty; if (!string.IsNullOrWhiteSpace(readingFilter)) { // Build the sql reading filter clause. // Example with readingFilter="かな" : // // WHERE v.KanaWriting LIKE '%かな%' OR // v.KanjiWriting LIKE '%かな%' sqlReadingFilter = string.Format("(v.{0} LIKE @reading OR v.{1} LIKE @reading) ", SqlHelper.Field_Vocab_KanaWriting, SqlHelper.Field_Vocab_KanjiWriting); parameters.Add(new DaoParameter("@reading", "%" + readingFilter + "%")); } string sqlSharedJoins = string.Empty; string sqlMeaningFilterJoins = string.Empty; string sqlMeaningFilter = string.Empty; if (!string.IsNullOrWhiteSpace(meaningFilter)) { // Build the sql meaning filter clause and join clauses. // Example of filter clause with meaningFilter="test" : // // WHERE vm.Meaning LIKE '%test%' // First, build the join clause if it does not already exist. This will be included before the filters. if (string.IsNullOrEmpty(sqlSharedJoins)) { sqlSharedJoins = joinString_VocabEntity_VocabMeaning; } sqlMeaningFilterJoins = joinString_VocabMeaningSet; // Once the join clauses are done, build the filter itself. sqlMeaningFilter = string.Format("vm.{0} LIKE @meaning ", SqlHelper.Field_VocabMeaning_Meaning); parameters.Add(new DaoParameter("@meaning", "%" + meaningFilter + "%")); } string sqlCategoryFilterJoins = string.Empty; string sqlCategoryFilter = string.Empty; if (categoryFilter != null) { // Build the filter clause for the vocab category. // Note that the category is actually associated either with the vocab itself or with a MEANING, // so we need to grab any vocab which itself has said category, or of which ONE OF THE MEANINGS // is of said category. // Example of filter clause with category.ID=42 : // // WHERE vc.Categories_ID=42 OR mc.Categories_ID=42 if (string.IsNullOrEmpty(sqlSharedJoins)) { sqlSharedJoins = joinString_VocabEntity_VocabMeaning; } /* TODO: Currently, only the meanings are checked for category matches, not vocab items themselves. * This is because of several reasons: * * 1) In the current database, not a single vocab has a category attached. * Only meanings do. * 2) Saving some performance by not doing a check that does not do anything. * * This does mean that this query must be changed if vocab items ever * get a category attached to them. * * Actually, scratch that. Ateji DO have a category attached to the vocab themselves. */ string subQuery = string.Format("(SELECT m.{0} FROM {1} m WHERE m.{2}=@cat)", SqlHelper.Field_VocabMeaning_VocabCategory_VocabMeaningId, SqlHelper.Table_VocabMeaning_VocabCategory, SqlHelper.Field_VocabMeaning_VocabCategory_VocabCategoryId); // First, build the join clause if it does not already exist. This will be included before the filters. sqlCategoryFilterJoins = string.IsNullOrEmpty(sqlMeaningFilterJoins) ? joinString_VocabMeaningSet : null; // Once the join clauses are done, build the filter itself. sqlCategoryFilter = string.Format("vm.{0} IN {1} ", SqlHelper.Field_VocabMeaning_Id, subQuery); parameters.Add(new DaoParameter("@cat", categoryFilter.ID)); } string[] sqlArgs = { sqlSharedJoins, sqlMeaningFilterJoins, sqlCategoryFilterJoins, sqlJlptFilter, sqlWkFilter, sqlKanjiFilter, sqlReadingFilter, sqlMeaningFilter, sqlCategoryFilter }; bool isFiltered = false; for (int i = 0; i < sqlArgs.Length; i++) { string arg = sqlArgs[i]; if (string.IsNullOrEmpty(arg) || arg.StartsWith("JOIN")) { continue; } sqlArgs[i] = (isFiltered ? "AND " : "WHERE ") + arg; isFiltered = true; } return(string.Concat(sqlArgs)); }
public void LoadFromKanji(KanjiEntity k) { Reference.LoadFromKanji(k); RaisePropertyChanged("Meanings"); RaisePropertyChanged("Readings"); }
/// <summary> /// Reads the KanjiDic2 file and outputs kanji entities parsed from the file. /// </summary> /// <returns>Kanji entities parsed from the file.</returns> private IEnumerable <KanjiEntity> ReadKanjiDic2() { // Load the KanjiDic2 file. XDocument xdoc = XDocument.Load(PathHelper.KanjiDic2Path); // Browse kanji nodes. foreach (XElement xkanji in xdoc.Root.Elements(XmlNode_Character)) { // For each kanji node, read values. KanjiEntity kanji = new KanjiEntity(); // Read the kanji character. kanji.Character = xkanji.Element(XmlNode_Literal).Value; // In the code point node... XElement xcodePoint = xkanji.Element(XmlNode_CodePoint); if (xcodePoint != null) { // Try to read the unicode character value. XElement xunicode = xcodePoint.Elements(XmlNode_CodePointValue) .Where(x => x.ReadAttributeString(XmlAttribute_CodePointType) == XmlAttributeValue_CodePointUnicode) .FirstOrDefault(); if (xunicode != null) { string unicodeValueString = xunicode.Value; int intValue = 0; if (int.TryParse(unicodeValueString, System.Globalization.NumberStyles.HexNumber, ParsingHelper.DefaultCulture, out intValue)) { kanji.UnicodeValue = intValue; } } } // In the misc node... XElement xmisc = xkanji.Element(XmlNode_Misc); if (xmisc != null) { // Try to read the grade, stroke count, frequency and JLPT level. // Update: JLPT level is outdated in this file. Now using the JLPTKanjiList. XElement xgrade = xmisc.Element(XmlNode_Grade); XElement xstrokeCount = xmisc.Element(XmlNode_StrokeCount); XElement xfrequency = xmisc.Element(XmlNode_Frequency); //XElement xjlpt = xmisc.Element(XmlNode_JlptLevel); if (xgrade != null) { kanji.Grade = ParsingHelper.ParseShort(xgrade.Value); } if (xstrokeCount != null) { kanji.StrokeCount = ParsingHelper.ParseShort(xstrokeCount.Value); } if (xfrequency != null) { kanji.NewspaperRank = ParsingHelper.ParseInt(xfrequency.Value); } //if (xjlpt != null) kanji.JlptLevel = ParsingHelper.ParseShort(xjlpt.Value); } // Find the JLPT level using the dictionary. if (_jlptDictionary.ContainsKey(kanji.Character)) { kanji.JlptLevel = _jlptDictionary[kanji.Character]; } // Find the frequency rank using the dictionary. if (_frequencyRankDictionary.ContainsKey(kanji.Character)) { kanji.MostUsedRank = _frequencyRankDictionary[kanji.Character]; } // Find the WaniKani level using the dictionary. if (_waniKaniDictionary.ContainsKey(kanji.Character)) { kanji.WaniKaniLevel = _waniKaniDictionary[kanji.Character]; } // In the reading/meaning node... XElement xreadingMeaning = xkanji.Element(XmlNode_ReadingMeaning); if (xreadingMeaning != null) { // Read the nanori readings. kanji.Nanori = string.Empty; foreach (XElement xnanori in xreadingMeaning.Elements(XmlNode_Nanori)) { kanji.Nanori += xnanori.Value + MultiValueFieldHelper.ValueSeparator; } kanji.Nanori = kanji.Nanori.Trim(MultiValueFieldHelper.ValueSeparator); // Browse the reading group... XElement xrmGroup = xreadingMeaning.Element(XmlNode_ReadingMeaningGroup); if (xrmGroup != null) { // Read the on'yomi readings. kanji.OnYomi = string.Empty; foreach (XElement xonYomi in xrmGroup.Elements(XmlNode_Reading) .Where(x => x.Attribute(XmlAttribute_ReadingType).Value == XmlAttributeValue_OnYomiReading)) { kanji.OnYomi += xonYomi.Value + MultiValueFieldHelper.ValueSeparator; } kanji.OnYomi = KanaHelper.ToHiragana(kanji.OnYomi.Trim(MultiValueFieldHelper.ValueSeparator)); // Read the kun'yomi readings. kanji.KunYomi = string.Empty; foreach (XElement xkunYomi in xrmGroup.Elements(XmlNode_Reading) .Where(x => x.Attribute(XmlAttribute_ReadingType).Value == XmlAttributeValue_KunYomiReading)) { kanji.KunYomi += xkunYomi.Value + MultiValueFieldHelper.ValueSeparator; } kanji.KunYomi = kanji.KunYomi.Trim(MultiValueFieldHelper.ValueSeparator); // Browse the meanings... foreach (XElement xmeaning in xrmGroup.Elements(XmlNode_Meaning)) { // Get the language and meaning. XAttribute xlanguage = xmeaning.Attribute(XmlAttribute_MeaningLanguage); string language = xlanguage != null?xlanguage.Value.ToLower() : null; string meaning = xmeaning.Value; if (xlanguage == null || language.ToLower() == "en") { // Build a meaning. KanjiMeaning kanjiMeaning = new KanjiMeaning() { Kanji = kanji, Language = language, Meaning = meaning }; // Add the meaning to the kanji. kanji.Meanings.Add(kanjiMeaning); } } } } // Return the kanji read and go to the next kanji node. yield return(kanji); xkanji.RemoveAll(); } }
/// <summary> /// Loads and returns vocab items from the JMDict XDocument. /// </summary> /// <param name="xdoc">Loaded JMDict XDocument.</param> /// <returns>Vocab loaded from the file.</returns> private IEnumerable <VocabEntity> LoadVocabItems(XDocument xdoc) { // Browse each vocab entry. foreach (XElement xentry in xdoc.Root.Elements(XmlNode_Entry)) { List <VocabEntity> vocabList = new List <VocabEntity>(); long seq = long.Parse(xentry.Element(XmlNode_EntSeq).Value); // For each kanji element node foreach (XElement xkanjiElement in xentry.Elements(XmlNode_KanjiElement)) { // Parse the kanji element. The list will be expanded with new elements. ParseKanji(xkanjiElement, vocabList, seq); } // For each kanji reading node var xreadingElements = xentry.Elements(XmlNode_ReadingElement); foreach (XElement xreadingElement in xreadingElements) { // Exclude the node if it contains the no kanji node, and is not the only reading. // This is a behavior that seems to be implemented in Jisho (example word: 台詞). if (xreadingElement.HasElement(XmlNode_NoKanji) && xreadingElements.Count() > 1) { continue; } // Parse the reading. The list will be expanded and/or its elements filled with // the available info. ParseReading(xreadingElement, vocabList, seq); } // For each kanji meaning node foreach (XElement xmeaningElement in xentry.Elements(XmlNode_Meaning)) { // Parse the meaning node. The list will be updated with the available info. ParseMeaning(xmeaningElement, vocabList); } // Now we will define the vocab <-> kanji relationships. // For each vocab bool first = true; foreach (VocabEntity vocab in vocabList) { // If the kanji vocab is defined if (!string.IsNullOrEmpty(vocab.KanjiWriting)) { // Browse each character foreach (char c in vocab.KanjiWriting) { // Attempt to find a kanji matching the character. string s = c.ToString(); KanjiEntity kanji = _kanjiDictionary.ContainsKey(s) ? _kanjiDictionary[s] : null; if (kanji != null) { // If a kanji is found, create a relationship between the entities. vocab.Kanji.Add(kanji); } } } if (first) { // Set the first vocab as the main variant. first = false; vocab.IsMain = true; } else { // For now, we are saying that non-main vocab cannot be common. // Otherwise, too much variants are marked as common and it can // be confusing. vocab.IsCommon = false; } // Return the vocab and continue to the next one. yield return(vocab); } } }
/// <summary> /// Builds and returns the vocab filter SQL clauses from the given /// filters. /// </summary> internal string BuildVocabFilterClauses(List <DaoParameter> parameters, KanjiEntity kanji, string readingFilter, string meaningFilter, VocabCategory categoryFilter, int jlptLevel, int wkLevel) { const int minJlptLevel = Levels.MinJlptLevel; const int maxJlptLevel = Levels.MaxJlptLevel; const int minWkLevel = Levels.MinWkLevel; const int maxWkLevel = Levels.MaxWkLevel; string sqlJlptFilter = string.Empty; if (jlptLevel >= minJlptLevel && jlptLevel <= maxJlptLevel) { sqlJlptFilter = string.Format("v.{0}=@jlpt ", SqlHelper.Field_Vocab_JlptLevel); parameters.Add(new DaoParameter("@jlpt", jlptLevel)); } else if (jlptLevel < minJlptLevel) { sqlJlptFilter = string.Format("v.{0} IS NULL ", SqlHelper.Field_Vocab_JlptLevel); } string sqlWkFilter = string.Empty; if (wkLevel >= minWkLevel && wkLevel <= maxWkLevel) { sqlWkFilter = string.Format("v.{0}=@wk ", SqlHelper.Field_Vocab_WaniKaniLevel); parameters.Add(new DaoParameter("@wk", wkLevel)); } else if (wkLevel > maxWkLevel) { sqlWkFilter = string.Format("v.{0} IS NULL ", SqlHelper.Field_Vocab_WaniKaniLevel); } string sqlKanjiFilter = string.Empty; if (kanji != null) { // Build the sql kanji filter clause. // Example with the kanji '達' : // // WHERE v.KanjiWriting LIKE '%達%' sqlKanjiFilter = string.Format("v.{0} LIKE @kanji ", SqlHelper.Field_Vocab_KanjiWriting); parameters.Add(new DaoParameter("@kanji", "%" + kanji.Character + "%")); } string sqlReadingFilter = string.Empty; if (!string.IsNullOrWhiteSpace(readingFilter)) { // Build the sql reading filter clause. // Example with readingFilter="かな" : // // WHERE v.KanaWriting LIKE '%かな%' OR // v.KanjiWriting LIKE '%かな%' sqlReadingFilter = string.Format("(v.{0} LIKE @reading OR v.{1} LIKE @reading) ", SqlHelper.Field_Vocab_KanaWriting, SqlHelper.Field_Vocab_KanjiWriting); parameters.Add(new DaoParameter("@reading", "%" + readingFilter + "%")); } string sqlSharedJoins = string.Empty; string sqlMeaningFilterJoins = string.Empty; string sqlMeaningFilter = string.Empty; if (!string.IsNullOrWhiteSpace(meaningFilter)) { // Build the sql meaning filter clause and join clauses. // Example of filter clause with meaningFilter="test" : // // WHERE vm.Meaning LIKE '%test%' // First, build the join clause. This will be included before the filters. if (string.IsNullOrEmpty(sqlSharedJoins)) { sqlSharedJoins = string.Format("JOIN {0} vvm ON (vvm.{1}=v.{2}) ", SqlHelper.Table_Vocab_VocabMeaning, SqlHelper.Field_Vocab_VocabMeaning_VocabId, SqlHelper.Field_Vocab_Id); } sqlMeaningFilterJoins = string.Format("JOIN {0} vm ON (vm.{1}=vvm.{2}) ", SqlHelper.Table_VocabMeaning, SqlHelper.Field_VocabMeaning_Id, SqlHelper.Field_Vocab_VocabMeaning_VocabMeaningId); // Ouch... it looks kinda like an obfuscated string... Sorry. // Basically, you just join the vocab to its meaning entries. // Once the join clauses are done, build the filter itself. sqlMeaningFilter = string.Format("vm.{0} LIKE @meaning ", SqlHelper.Field_VocabMeaning_Meaning); parameters.Add(new DaoParameter("@meaning", "%" + meaningFilter + "%")); } string sqlCategoryFilterJoins = string.Empty; string sqlCategoryFilter = string.Empty; if (categoryFilter != null) { // Build the filter clause for the vocab category. // Note that the category is actually associated either with the vocab itself or with a MEANING, // so we need to grab any vocab which itself has said category, or of which ONE OF THE MEANINGS // is of said category. // Example of filter clause with category.ID=42 : // // WHERE vc.Categories_ID=42 OR mc.Categories_ID=42 if (string.IsNullOrEmpty(sqlSharedJoins)) { sqlSharedJoins = string.Format("JOIN {0} vvm ON (vvm.{1}=v.{2}) ", SqlHelper.Table_Vocab_VocabMeaning, SqlHelper.Field_Vocab_VocabMeaning_VocabId, SqlHelper.Field_Vocab_Id); } sqlCategoryFilterJoins = string.Format( "JOIN {0} vc ON (vc.{1}=v.{2}) JOIN {3} mc ON (mc.{4}=vvm.{5}) ", SqlHelper.Table_VocabCategory_Vocab, SqlHelper.Field_VocabCategory_Vocab_VocabId, SqlHelper.Field_VocabCategory_Id, SqlHelper.Table_VocabMeaning_VocabCategory, SqlHelper.Field_VocabMeaning_VocabCategory_VocabMeaningId, SqlHelper.Field_Vocab_VocabMeaning_VocabMeaningId); sqlCategoryFilter = string.Format("(vc.{0}=@cat OR mc.{1}=@cat) ", SqlHelper.Field_VocabCategory_Vocab_VocabCategoryId, SqlHelper.Field_VocabMeaning_VocabCategory_VocabCategoryId); parameters.Add(new DaoParameter("@cat", categoryFilter.ID)); } string[] sqlArgs = { sqlSharedJoins, sqlMeaningFilterJoins, sqlCategoryFilterJoins, sqlJlptFilter, sqlWkFilter, sqlKanjiFilter, sqlReadingFilter, sqlMeaningFilter, sqlCategoryFilter }; bool isFiltered = false; for (int i = 0; i < sqlArgs.Length; i++) { string arg = sqlArgs[i]; if (string.IsNullOrEmpty(arg) || arg.StartsWith("JOIN")) { continue; } sqlArgs[i] = (isFiltered ? "AND " : "WHERE ") + arg; isFiltered = true; } return(string.Concat(sqlArgs)); }