/// <summary> /// Gets the data for one custom field, and any relevant GUIDs. /// </summary> /// <param name="hvo">Hvo of object we're getting the field for.</param> /// <param name="flid">Flid for this field.</param> /// <param name="fieldSourceType">Either "entry", "senses" or "examples". Could also be "allomorphs", eventually.</param> /// <param name="bsonForThisField">Output of a BsonDocument with the following structure: <br /> /// { fieldName: { "value": BsonValue, "guid": "some-guid-as-a-string" } } <br /> /// -OR- <br /> /// { fieldName: { "value": BsonValue, "guid": ["guid1", "guid2", "guid3"] } } <br /> /// The format of the fieldName key will be "customField_FOO_field_name_with_underscores", /// where FOO is one of "entry", "senses", or "examples". <br /> /// The type of the "guid" value (array or string) will determine whether there is a single GUID, /// or a list of GUIDs that happens to contain only one entry. /// If there is no "guid" key, that field has no need for a GUID. (E.g., a number). /// </param> /// <param name="listConverters">Dictionary of ConvertLcmToMongoOptionList instances, keyed by list code</param> private BsonDocument GetCustomFieldData(int hvo, int flid, string fieldSourceType, IDictionary <string, ConvertLcmToMongoOptionList> listConverters) { BsonValue fieldValue = null; BsonValue fieldGuid = null; // Might be a single value, might be a list (as a BsonArray) ISilDataAccessManaged data = (ISilDataAccessManaged)cache.DomainDataByFlid; CellarPropertyType LcmFieldType = (CellarPropertyType)LcmMetaData.GetFieldType(flid); var dataGuids = new List <Guid>(); // Valid field types in Lcm are GenDate, Integer, String, OwningAtomic, ReferenceAtomic, and ReferenceCollection, so that's all we implement. switch (LcmFieldType) { case CellarPropertyType.GenDate: GenDate genDate = data.get_GenDateProp(hvo, flid); string genDateStr = genDate.ToLongString(); // LF wants single-string fields in the format { "ws": { "value": "contents" } } fieldValue = String.IsNullOrEmpty(genDateStr) ? null : LfMultiText.FromSingleStringMapping( MagicStrings.LanguageCodeForGenDateFields, genDateStr).AsBsonDocument(); break; // When parsing, will use GenDate.TryParse(str, out genDate) case CellarPropertyType.Integer: fieldValue = new BsonInt32(data.get_IntProp(hvo, flid)); if (fieldValue.AsInt32 == default(Int32)) { fieldValue = null; // Suppress int fields with 0 in them, to save Mongo DB space } else { // LF wants single-string fields in the format { "ws": { "value": "contents" } } fieldValue = LfMultiText.FromSingleStringMapping( MagicStrings.LanguageCodeForIntFields, fieldValue.AsInt32.ToString()).AsBsonDocument(); } break; case CellarPropertyType.OwningAtomic: case CellarPropertyType.ReferenceAtomic: int ownedHvo = data.get_ObjectProp(hvo, flid); fieldValue = GetCustomReferencedObject(ownedHvo, flid, listConverters, ref dataGuids); if (fieldValue != null && LcmFieldType == CellarPropertyType.ReferenceAtomic) { // Single CmPossiblity reference - LF expects format like { "value": "key of possibility" } fieldValue = new BsonDocument("value", fieldValue); } fieldGuid = new BsonString(dataGuids.FirstOrDefault().ToString()); break; case CellarPropertyType.MultiUnicode: ITsMultiString tss = data.get_MultiStringProp(hvo, flid); if (tss != null && tss.StringCount > 0) { fieldValue = LfMultiText.FromMultiITsString(tss, servLoc.WritingSystemManager).AsBsonDocument(); } break; case CellarPropertyType.OwningCollection: case CellarPropertyType.OwningSequence: case CellarPropertyType.ReferenceCollection: case CellarPropertyType.ReferenceSequence: int[] listHvos = data.VecProp(hvo, flid); var innerValues = new BsonArray(listHvos.Select(listHvo => GetCustomReferencedObject(listHvo, flid, listConverters, ref dataGuids)).Where(x => x != null)); if (innerValues.Count == 0) { fieldValue = null; } else { fieldValue = new BsonDocument("values", innerValues); fieldGuid = new BsonArray(dataGuids.Select(guid => guid.ToString())); } break; case CellarPropertyType.String: ITsString iTsValue = data.get_StringProp(hvo, flid); if (iTsValue == null || String.IsNullOrEmpty(iTsValue.Text)) { fieldValue = null; } else { fieldValue = LfMultiText.FromSingleITsString(iTsValue, servLoc.WritingSystemManager).AsBsonDocument(); } break; default: fieldValue = null; if (logger != null) { logger.Warning("Lcm CellarPropertyType.{0} not recognized for LF custom field", LcmFieldType.ToString()); } break; } if (fieldValue == null) { return(null); } else { var result = new BsonDocument(); result.Add("value", fieldValue ?? BsonNull.Value); // BsonValues aren't allowed to have C# nulls; they have their own null representation if (fieldGuid is BsonArray) { result.Add("guid", fieldGuid, ((BsonArray)fieldGuid).Count > 0); } else { result.Add("guid", fieldGuid, fieldGuid != null); } return(result); } }
public void WriteCustomFieldConfigForOneFieldSourceType( int classId, string fieldSourceType, // Can be either "entry", "senses", or "examples" Dictionary <string, LfConfigFieldBase> lfCustomFieldConfig, Dictionary <string, string> lfCustomFieldTypes ) { IEnumerable <int> customFieldIds = LcmMetaData.GetFields(classId, false, (int)CellarPropertyTypeFilter.All) .Where(flid => cache.GetIsCustomField(flid)); foreach (int flid in customFieldIds) { string label = LcmMetaData.GetFieldNameOrNull(flid); if (label == null) { continue; } string lfCustomFieldName = ConvertUtilities.NormalizedFieldName(label, fieldSourceType); CellarPropertyType LcmFieldType = (CellarPropertyType)LcmMetaData.GetFieldType(flid); lfCustomFieldTypes[lfCustomFieldName] = LcmFieldType.ToString(); string lfCustomFieldType; if (CellarPropertyTypeToLfCustomFieldType.TryGetValue(LcmFieldType, out lfCustomFieldType)) { // Get custom field configuration info LfConfigFieldBase fieldConfig = null; if (lfCustomFieldType.EndsWith("ListRef")) { // List references, whether single or multi, need a list code string listCode = GetParentListCode(flid); fieldConfig = GetLfCustomFieldOptionListConfig(label, lfCustomFieldType, listCode); } else if (lfCustomFieldType == CellarPropertyTypeToLfCustomFieldType[CellarPropertyType.OwningAtomic]) { // Multiparagraphs don't need writing systems fieldConfig = GetLfCustomFieldMultiParagraphConfig(label, lfCustomFieldType); } else { // Single line or MultiText fields need writing systems int fieldWs = LcmMetaData.GetFieldWs(flid); // That's a "magic" ws, which we need to expand into a (list of) real writing system(s). #if FW8_COMPAT var wsesForThisField = new List <IWritingSystem>(); #else var wsesForThisField = new List <CoreWritingSystemDefinition>(); #endif // GetWritingSystemList() in FW 8.3 is buggy and doesn't properly handle the kwsAnal and kwsVern cases, so we handle them here instead. switch (fieldWs) { case WritingSystemServices.kwsAnal: wsesForThisField.Add(servLoc.LanguageProject.DefaultAnalysisWritingSystem); break; case WritingSystemServices.kwsVern: wsesForThisField.Add(servLoc.LanguageProject.DefaultVernacularWritingSystem); break; default: wsesForThisField = WritingSystemServices.GetWritingSystemList(cache, fieldWs, forceIncludeEnglish: false); break; } #if FW8_COMPAT IEnumerable <string> inputSystems = wsesForThisField.Select(LcmWs => LcmWs.Id); #else IEnumerable <string> inputSystems = wsesForThisField.Select(LcmWs => LcmWs.LanguageTag); #endif // GetWritingSystemList returns all analysis WSes even when asked for just one, so if this // is a single-line custom field, trim the WSes down to just the first one if (lfCustomFieldType.StartsWith("Single")) { inputSystems = inputSystems.Take(1); } fieldConfig = GetLfCustomFieldMultiTextConfig(label, lfCustomFieldType, inputSystems.ToList()); } if (fieldConfig != null) { lfCustomFieldConfig[lfCustomFieldName] = fieldConfig; } } } }