/// <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; }
/// <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> /// 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> /// 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) { bool isFiltered = false; string sqlKanjiFilter = string.Empty; if (kanji != null) { // Build the sql kanji filter clause. // Example with the kanji '達' : // // WHERE v.KanjiWriting LIKE '%達%' isFiltered = true; sqlKanjiFilter = "WHERE v." + SqlHelper.Field_Vocab_KanjiWriting + " LIKE @kanji "; 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 = isFiltered ? "AND " : "WHERE "; isFiltered = true; sqlReadingFilter += "(v." + SqlHelper.Field_Vocab_KanaWriting + " LIKE @reading OR v." + SqlHelper.Field_Vocab_KanjiWriting + " LIKE @reading) "; parameters.Add(new DaoParameter("@reading", "%" + readingFilter + "%")); } 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 vme.Meaning LIKE '%test%' // First, build the join clause. This will be included before the filters. sqlMeaningFilterJoins = "JOIN " + SqlHelper.Table_Vocab_VocabMeaning + " vvm ON (vvm." + SqlHelper.Field_Vocab_VocabMeaning_VocabId + "=v." + SqlHelper.Field_Vocab_Id + ") " + "JOIN " + SqlHelper.Table_VocabMeaning + " vm ON (vm." + SqlHelper.Field_VocabMeaning_Id + "=vvm." + 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. // This will be applied as the last filter. sqlMeaningFilter = isFiltered ? "AND " : "WHERE "; isFiltered = true; sqlMeaningFilter += "vm." + SqlHelper.Field_VocabMeaning_Meaning + " LIKE @meaning "; parameters.Add(new DaoParameter("@meaning", "%" + meaningFilter + "%")); } return sqlMeaningFilterJoins + sqlKanjiFilter + sqlReadingFilter + sqlMeaningFilter; }
/// <summary> /// See <see cref="Kanji.Database.Dao.VocabDao.GetFilteredVocab"/>. /// Returns the results count. /// </summary> public long GetFilteredVocabCount(KanjiEntity kanji, string readingFilter, string meaningFilter) { List<DaoParameter> parameters = new List<DaoParameter>(); string sqlFilterClauses = BuildVocabFilterClauses(parameters, kanji, readingFilter, meaningFilter); using (DaoConnection connection = DaoConnection.Open(DaoConnectionEnum.KanjiDatabase)) { return (long)connection.QueryScalar( "SELECT count(1) FROM " + SqlHelper.Table_Vocab + " v " + sqlFilterClauses, parameters.ToArray()); } }
/// <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="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, bool isCommonFirst, bool isShortWritingFirst) { List<DaoParameter> parameters = new List<DaoParameter>(); string sqlFilterClauses = BuildVocabFilterClauses(parameters, kanji, readingFilter, meaningFilter); string sortClause = "ORDER BY "; if (isCommonFirst) { sortClause += "v." + SqlHelper.Field_Vocab_IsCommon + " DESC,"; } sortClause += "length(v." + 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( "SELECT DISTINCT v.* FROM " + SqlHelper.Table_Vocab + " v " + 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> /// 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( "SELECT * " + "FROM " + SqlHelper.Table_SrsEntry + " srs " + "WHERE srs." + SqlHelper.Field_SrsEntry_AssociatedKanji + "=@k", new DaoParameter("@k", kanji.Character)); SrsEntryBuilder srsEntryBuilder = new SrsEntryBuilder(); foreach (NameValueCollection nvcEntry in nvcEntries) { kanji.SrsEntries.Add(srsEntryBuilder.BuildEntity(nvcEntry, null)); } }
/// <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( "SELECT * " + "FROM " + SqlHelper.Table_Radical + " r " + "JOIN " + SqlHelper.Table_Kanji_Radical + " kr " + "ON (kr." + SqlHelper.Field_Kanji_Radical_RadicalId + "=r." + SqlHelper.Field_Radical_Id + ") " + "WHERE kr." + SqlHelper.Field_Kanji_Radical_KanjiId + "=@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> /// 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( "SELECT * " + "FROM " + SqlHelper.Table_KanjiMeaning + " km " + "WHERE km." + SqlHelper.Field_KanjiMeaning_KanjiId + "=@kanjiId " + "AND km." + SqlHelper.Field_KanjiMeaning_Language + " IS NULL;", 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); } }
public ExtendedKanji(KanjiEntity dbKanji) { DbKanji = dbKanji; RadicalStore.Instance.IssueWhenLoaded(OnRadicalsLoaded); }
public void LoadFromKanji(KanjiEntity k) { Reference.LoadFromKanji(k); RaisePropertyChanged("Meanings"); RaisePropertyChanged("Readings"); }